From f4a93cba38f91e0a83fe408d0d511145abd22d20 Mon Sep 17 00:00:00 2001 From: Jason Stirnaman Date: Thu, 25 Sep 2025 11:59:51 -0500 Subject: [PATCH 1/9] chore(qol): Instruction to use /version/ in shared links --- .github/instructions/content.instructions.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/instructions/content.instructions.md b/.github/instructions/content.instructions.md index 3467792cdf..cd6c3019fe 100644 --- a/.github/instructions/content.instructions.md +++ b/.github/instructions/content.instructions.md @@ -208,6 +208,23 @@ When building shared content, use the `show-in` and `hide-in` shortcodes to show or hide blocks of content based on the current InfluxDB product/version. For more information, see [show-in](#show-in) and [hide-in](#hide-in). +#### Links in shared content + +When creating links in shared content files, use `/influxdb3/version/` instead of the `{{% product-key %}}` shortcode. +The keyword `version` gets replaced during the build process with the appropriate product version. + +**Use this in shared content:** +```markdown +[Configuration options](/influxdb3/version/reference/config-options/) +[CLI serve command](/influxdb3/version/reference/cli/influxdb3/serve/) +``` + +**Not this:** +```markdown +[Configuration options](/influxdb3/{{% product-key %}}/reference/config-options/) +[CLI serve command](/influxdb3/{{% product-key %}}/reference/cli/influxdb3/serve/) +``` + #### Shortcodes in Markdown files For the complete shortcodes reference, see `/.github/instructions/shortcodes-reference.instructions.md`. @@ -219,4 +236,4 @@ For the complete shortcodes reference, see `/.github/instructions/shortcodes-ref - Format code examples to fit within 80 characters - Use long options in command line examples (`--option` instead of `-o`) - Use GitHub callout syntax for notes and warnings -- Image naming: `project/version-context-description.png` \ No newline at end of file +- Image naming: `project/version-context-description.png` From 113ac167cb0a39afa15f51b58c7619fc074c194e Mon Sep 17 00:00:00 2001 From: Jason Stirnaman Date: Fri, 26 Sep 2025 16:04:58 -0500 Subject: [PATCH 2/9] feat(influxdb3): Core and Ent. metrics reference and monitoring.- Add reference doc for /metrics output.- Add monitoring guide: - Core and general metrics - Enterprise cluster and node-specific metrics - Using metrics and relabeling using Prometheus or Telegraf. --- .../influxdb3/core/admin/monitor-metrics.md | 22 + content/influxdb3/core/reference/metrics.md | 22 + .../enterprise/admin/monitor-metrics.md | 24 + .../influxdb3/enterprise/reference/metrics.md | 23 + .../shared/influxdb3-admin/monitor-metrics.md | 639 ++++++++++++++++++ content/shared/influxdb3-reference/metrics.md | 581 ++++++++++++++++ 6 files changed, 1311 insertions(+) create mode 100644 content/influxdb3/core/admin/monitor-metrics.md create mode 100644 content/influxdb3/core/reference/metrics.md create mode 100644 content/influxdb3/enterprise/admin/monitor-metrics.md create mode 100644 content/influxdb3/enterprise/reference/metrics.md create mode 100644 content/shared/influxdb3-admin/monitor-metrics.md create mode 100644 content/shared/influxdb3-reference/metrics.md diff --git a/content/influxdb3/core/admin/monitor-metrics.md b/content/influxdb3/core/admin/monitor-metrics.md new file mode 100644 index 0000000000..1bf3ee6c8a --- /dev/null +++ b/content/influxdb3/core/admin/monitor-metrics.md @@ -0,0 +1,22 @@ +--- +title: Monitor metrics +seotitle: Monitor InfluxDB 3 Core metrics +description: > + Access and understand Prometheus-format metrics exposed by {{< product-name >}} + to monitor system performance, resource usage, and operational health. +menu: + influxdb3_core: + parent: Administer InfluxDB + name: Monitor metrics +weight: 105 +influxdb3/core/tags: [monitoring, metrics, prometheus, observability, operations] +related: + - /influxdb3/core/reference/internals/runtime-architecture/ + - /influxdb3/core/admin/performance-tuning/ + - /influxdb3/core/reference/telemetry/ +source: /shared/influxdb3-admin/monitor-metrics.md +--- + + \ No newline at end of file diff --git a/content/influxdb3/core/reference/metrics.md b/content/influxdb3/core/reference/metrics.md new file mode 100644 index 0000000000..c73998a9ad --- /dev/null +++ b/content/influxdb3/core/reference/metrics.md @@ -0,0 +1,22 @@ +--- +title: Metrics +seotitle: InfluxDB 3 Core metrics reference +description: > + InfluxDB 3 Core exposes Prometheus-format metrics, + including descriptions, types, and labels for monitoring and observability. +menu: + influxdb3_core: + parent: Reference +weight: 106 +influxdb3/core/tags: [metrics, prometheus, monitoring, reference, observability] +related: + - /influxdb3/core/admin/monitor-metrics/ + - /influxdb3/core/reference/telemetry/ + - /influxdb3/core/reference/internals/runtime-architecture/ +source: /shared/influxdb3-reference/metrics.md +--- + + \ No newline at end of file diff --git a/content/influxdb3/enterprise/admin/monitor-metrics.md b/content/influxdb3/enterprise/admin/monitor-metrics.md new file mode 100644 index 0000000000..68375f0096 --- /dev/null +++ b/content/influxdb3/enterprise/admin/monitor-metrics.md @@ -0,0 +1,24 @@ +--- +title: Monitor metrics +seotitle: Monitor {{< product-name >}} metrics +description: > + Access and understand Prometheus-format metrics exposed by {{< product-name >}} + to monitor distributed cluster performance, resource usage, and operational health. +menu: + influxdb3_enterprise: + parent: Administer InfluxDB + name: Monitor metrics +weight: 105 +influxdb3/enterprise/tags: [monitoring, metrics, prometheus, observability, operations, clustering] +related: + - /influxdb3/enterprise/admin/clustering/ + - /influxdb3/enterprise/reference/internals/runtime-architecture/ + - /influxdb3/enterprise/admin/performance-tuning/ + - /influxdb3/enterprise/reference/telemetry/ +source: /shared/influxdb3-admin/monitor-metrics.md +--- + + \ No newline at end of file diff --git a/content/influxdb3/enterprise/reference/metrics.md b/content/influxdb3/enterprise/reference/metrics.md new file mode 100644 index 0000000000..b9065d76e2 --- /dev/null +++ b/content/influxdb3/enterprise/reference/metrics.md @@ -0,0 +1,23 @@ +--- +title: Metrics +seotitle: InfluxDB 3 Enterprise metrics reference +description: > + InfluxDB 3 Enterprise exposes Prometheus-format metrics, + including descriptions, types, and labels for monitoring and observability. +menu: + influxdb3_enterprise: + parent: Reference +weight: 106 +influxdb3/enterprise/tags: [metrics, prometheus, monitoring, reference, observability, clustering] +related: + - /influxdb3/enterprise/admin/monitor-metrics/ + - /influxdb3/enterprise/admin/clustering/ + - /influxdb3/enterprise/reference/telemetry/ + - /influxdb3/enterprise/reference/internals/runtime-architecture/ +source: /shared/influxdb3-reference/metrics.md +--- + + \ No newline at end of file diff --git a/content/shared/influxdb3-admin/monitor-metrics.md b/content/shared/influxdb3-admin/monitor-metrics.md new file mode 100644 index 0000000000..ca1688960a --- /dev/null +++ b/content/shared/influxdb3-admin/monitor-metrics.md @@ -0,0 +1,639 @@ +Use InfluxDB metrics to monitor {{% show-in "enterprise" %}}distributed cluster {{% /show-in %}}system performance, resource usage, and operational health +with monitoring tools like Prometheus, Grafana, or other observability platforms. + +## Access metrics + +An {{< product-name >}} node exposes metrics at the `/metrics` endpoint on the HTTP port (default: 8181). + +{{% api-endpoint method="GET" endpoint="http://localhost:8181/metrics" api-ref="/influxdb3/version/api/v3/#operation/GetMetrics"%}} + +{{% show-in "core" %}} +### View metrics + +```bash +# View all metrics +curl -s http://{{< influxdb/host >}}/metrics + +# View specific metric patterns +curl -s http://{{< influxdb/host >}}/metrics | grep 'http_requests_total' +curl -s http://{{< influxdb/host >}}/metrics | grep 'influxdb3_' + +# View metrics with authentication (if required) +curl -s -H "Authorization: Token AUTH_TOKEN" http://node:8181/metrics +``` +{{% /show-in %}} + +{{% show-in "enterprise" %}} +### View metrics from specific nodes + +```bash { placeholders="AUTH_TOKEN" } +# View metrics from specific nodes +curl -s http://ingester-01:8181/metrics +curl -s http://query-01:8181/metrics +curl -s http://compactor-01:8181/metrics + +# View metrics with authentication (if required) +curl -s -H "Authorization: Token AUTH_TOKEN" http://node:8181/metrics +``` +{{% /show-in %}} + +Replace {{% code-placeholder-key %}}`AUTH_TOKEN`{{% /code-placeholder-key %}} with your {{< product-name >}} {{% token-link %}} that has read access to the `/metrics` endpoint. + +{{% show-in "enterprise" %}} +### Aggregate metrics across cluster + +```bash +# Get metrics from all nodes in cluster +for node in ingester-01 query-01 compactor-01; do + echo "=== Node: $node ===" + curl -s http://$node:8181/metrics | grep 'http_requests_total.*status="ok"' +done +``` +{{% /show-in %}} + +### Metrics format + +Metrics are exposed in [Prometheus exposition format](https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format): + +``` +# HELP metric_name Description of the metric +# TYPE metric_name counter|gauge|histogram +metric_name{label1="value1",label2="value2"} 42.0 +``` + +## Metric categories + +{{< product-name >}} exposes the following{{% show-in "enterprise" %}} base{{% /show-in %}} categories of metrics{{% show-in "enterprise" %}}, plus additional cluster-aware metrics{{% /show-in %}}: + +### HTTP and gRPC metrics + +Monitor API request patterns{{% show-in "enterprise" %}} across the cluster{{% /show-in %}}: + +- **`http_requests_total`**: Total HTTP requests by method, path, and status{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **`http_request_duration_seconds`**: HTTP request latency distribution{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **`http_response_body_size_bytes`**: HTTP response size distribution +- **`grpc_requests_total`**: Total gRPC requests{{% show-in "enterprise" %}} for inter-node communication{{% /show-in %}} +- **`grpc_request_duration_seconds`**: gRPC request latency distribution + +### Database operations + +Monitor database{{% show-in "enterprise" %}}-specific and distributed cluster{{% /show-in %}} operations: + +- **`influxdb3_catalog_operations_total`**: Catalog operations by type (create_database, create_admin_token, etc.){{% show-in "enterprise" %}} across the cluster{{% /show-in %}} +- **`influxdb3_catalog_operation_retries_total`**: Failed catalog operations that required retries{{% show-in "enterprise" %}} due to conflicts between nodes{{% /show-in %}} + +{{% show-in "enterprise" %}} +### Node specialization metrics + +Different metrics are more relevant depending on node [mode configuration](/influxdb3/version/admin/clustering/#configure-node-modes): + +#### Ingest nodes (mode: ingest) +- **`http_requests_total{path="/api/v3/write"}`**: Write request volume +- **`object_store_transfer_bytes_total`**: WAL-to-Parquet snapshot activity +- **`datafusion_mem_pool_bytes`**: Memory usage for snapshot operations + +#### Query nodes (mode: query) +- **`influxdb_iox_query_log_*`**: Query execution performance +- **`influxdb3_parquet_cache_*`**: Cache performance for query acceleration +- **`http_requests_total{path~"/api/v3/query.*"}`**: Query request patterns + +#### Compactor nodes (mode: compact) +- **`object_store_op_duration_seconds`**: Compaction operation performance +- **`object_store_transfer_*`**: File consolidation activity + +#### Process nodes (mode: process) +- **`tokio_runtime_*`**: Plugin execution runtime metrics +- Custom plugin metrics (varies by installed plugins) +{{% /show-in %}} + +### Memory and caching + +Monitor memory usage{{% show-in "enterprise" %}} across specialized nodes{{% /show-in %}}: + +- **`datafusion_mem_pool_bytes`**: DataFusion memory pool{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **`influxdb3_parquet_cache_access_total`**: Parquet cache hits, misses, and fetch status{{% show-in "enterprise" %}} per query node{{% /show-in %}} +- **`influxdb3_parquet_cache_size_bytes`**: Current size of in-memory Parquet cache{{% show-in "enterprise" %}} per query node{{% /show-in %}} +- **`influxdb3_parquet_cache_size_number_of_files`**: Number of files in Parquet cache{{% show-in "enterprise" %}} per query node{{% /show-in %}} +- **`jemalloc_memstats_bytes`**: Memory allocation statistics{{% show-in "enterprise" %}} per node{{% /show-in %}} + +### Query performance + +Monitor{{% show-in "enterprise" %}} distributed{{% /show-in %}} query execution{{% show-in "enterprise" %}} and performance{{% /show-in %}}: + +- **`influxdb_iox_query_log_*`**: Comprehensive query execution metrics including: + - `compute_duration_seconds`: CPU time spent on computation + - `execute_duration_seconds`: Total query execution time + - `plan_duration_seconds`: Time spent planning queries + - `end2end_duration_seconds`: Complete query duration from request to response + - `max_memory`: Peak memory usage per query + - `parquet_files`: Number of Parquet files accessed + - `partitions`: Number of partitions processed + +{{% show-in "enterprise" %}} +- **`influxdb_iox_query_log_ingester_latency_*`**: Inter-node query coordination latency +- **`influxdb_iox_query_log_ingester_partition_count`**: Data distribution across nodes +- **`influxdb_iox_query_log_parquet_files`**: File access patterns per query +{{% /show-in %}} + +### Object storage + +Monitor{{% show-in "enterprise" %}} shared{{% /show-in %}} object store operations and performance: + +- **`object_store_op_duration_seconds`**: Object store operation latency{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **`object_store_transfer_bytes_total`**: Cumulative bytes transferred{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **`object_store_transfer_objects_total`**: Cumulative objects transferred{{% show-in "enterprise" %}} per node{{% /show-in %}} + +### Runtime and system + +Monitor runtime health and resource usage: + +- **`process_start_time_seconds`**: Process start time +- **`thread_panic_count_total`**: Thread panic occurrences +- **`query_datafusion_query_execution_ooms_total`**: Out-of-memory events in query engine +- **`tokio_runtime_*`**: Async runtime metrics (task scheduling, worker threads, queue depths) + +{{% show-in "enterprise" %}} +## Cluster-specific metrics + +### Node coordination + +Monitor how nodes work together: + +```bash +# Check ingester response coordination +curl -s http://query-node:8181/metrics | grep 'influxdb_iox_query_log_ingester_latency' + +# Monitor catalog operation conflicts +curl -s http://any-node:8181/metrics | grep 'influxdb3_catalog_operation_retries_total' +``` + +### Load distribution + +Monitor workload distribution across nodes: + +```bash +# Write load across ingest nodes +for node in ingester-01 ingester-02; do + echo "Node $node:" + curl -s http://$node:8181/metrics | grep 'http_requests_total.*v3/write.*status="ok"' +done + +# Query load across query nodes +for node in query-01 query-02; do + echo "Node $node:" + curl -s http://$node:8181/metrics | grep 'influxdb_iox_query_log_execute_duration_seconds_count' +done +``` + +## Node-specific monitoring + +### Monitor ingest node health + +Monitor data ingestion performance: + +```bash +# Ingest throughput +curl -s http://ingester-01:8181/metrics | grep 'http_requests_total.*v3/write' + +# Snapshot creation activity +curl -s http://ingester-01:8181/metrics | grep 'object_store_transfer_bytes_total.*put' + +# Memory pressure +curl -s http://ingester-01:8181/metrics | grep 'datafusion_mem_pool_bytes' +``` + +### Monitor query node performance + +Monitor query execution: + +```bash +# Query latency +curl -s http://query-01:8181/metrics | grep 'influxdb_iox_query_log_execute_duration_seconds' + +# Cache effectiveness +curl -s http://query-01:8181/metrics | grep 'influxdb3_parquet_cache_access_total' + +# Inter-node coordination time +curl -s http://query-01:8181/metrics | grep 'influxdb_iox_query_log_ingester_latency' +``` + +### Monitor compactor node activity + +Monitor data optimization: + +```bash +# Compaction operations +curl -s http://compactor-01:8181/metrics | grep 'object_store_op_duration_seconds.*put' + +# File processing volume +curl -s http://compactor-01:8181/metrics | grep 'object_store_transfer_objects_total' +``` +{{% /show-in %}} + +{{% show-in "core" %}} +## Key metrics for monitoring + +### Write throughput + +Monitor data ingestion: + +```bash +# HTTP requests to write endpoints +curl -s http://localhost:8181/metrics | grep 'http_requests_total.*v3/write\|http_requests_total.*v2/write' + +# Object store writes (Parquet file creation) +curl -s http://localhost:8181/metrics | grep 'object_store_transfer.*total.*put' +``` + +### Query performance + +Monitor query execution: + +```bash +# Query latency percentiles +curl -s http://localhost:8181/metrics | grep 'influxdb_iox_query_log_execute_duration_seconds' + +# Query memory usage +curl -s http://localhost:8181/metrics | grep 'influxdb_iox_query_log_max_memory' + +# Query errors and failures +curl -s http://localhost:8181/metrics | grep 'http_requests_total.*status="server_error"' +``` + +### Resource utilization + +Monitor system resources: + +```bash +# Memory pool usage +curl -s http://localhost:8181/metrics | grep 'datafusion_mem_pool_bytes' + +# Cache efficiency +curl -s http://localhost:8181/metrics | grep 'influxdb3_parquet_cache_access_total' + +# Runtime task health +curl -s http://localhost:8181/metrics | grep 'tokio_runtime_num_alive_tasks' +``` + +### Error rates + +Monitor system health: + +```bash +# HTTP error rates +curl -s http://localhost:8181/metrics | grep 'http_requests_total.*status="client_error"\|http_requests_total.*status="server_error"' + +# Thread panics +curl -s http://localhost:8181/metrics | grep 'thread_panic_count_total' + +# Query OOMs +curl -s http://localhost:8181/metrics | grep 'query_datafusion_query_execution_ooms_total' +``` +{{% /show-in %}} + +## Example monitoring queries + +### Prometheus queries{{% show-in "enterprise" %}} for clusters{{% /show-in %}} + +Use these queries in Prometheus or Grafana dashboards: + +{{% show-in "enterprise" %}} +#### Cluster-wide request rate + +```promql +# Total requests per second across all nodes +sum(rate(http_requests_total[5m])) by (instance) + +# Write requests per second by ingest node +sum(rate(http_requests_total{path="/api/v3/write"}[5m])) by (instance) +``` + +#### Query performance across nodes + +```promql +# 95th percentile query latency by query node +histogram_quantile(0.95, + sum(rate(influxdb_iox_query_log_execute_duration_seconds_bucket[5m])) by (instance, le) +) + +# Average inter-node coordination time +avg(rate(influxdb_iox_query_log_ingester_latency_to_full_data_seconds_sum[5m]) / + rate(influxdb_iox_query_log_ingester_latency_to_full_data_seconds_count[5m])) by (instance) +``` + +#### Load balancing effectiveness + +```promql +# Request distribution balance (coefficient of variation) +stddev(sum(rate(http_requests_total[5m])) by (instance)) / +avg(sum(rate(http_requests_total[5m])) by (instance)) + +# Cache hit rate by query node +sum(rate(influxdb3_parquet_cache_access_total{status="cached"}[5m])) by (instance) / +sum(rate(influxdb3_parquet_cache_access_total[5m])) by (instance) +``` + +#### Cluster health indicators + +```promql +# Node availability (any recent metrics) +up{job="influxdb3-enterprise"} + +# Catalog operation conflicts +rate(influxdb3_catalog_operation_retries_total[5m]) + +# Cross-node error rates +sum(rate(http_requests_total{status=~"server_error|client_error"}[5m])) by (instance, status) +``` +{{% /show-in %}} + +{{% show-in "core" %}} +#### Request rate + +```promql +# Requests per second +rate(http_requests_total[5m]) + +# Error rate percentage +rate(http_requests_total{status=~"client_error|server_error"}[5m]) / rate(http_requests_total[5m]) * 100 +``` + +#### Query performance + +```promql +# 95th percentile query latency +histogram_quantile(0.95, rate(influxdb_iox_query_log_execute_duration_seconds_bucket[5m])) + +# Average query memory usage +rate(influxdb_iox_query_log_max_memory_sum[5m]) / rate(influxdb_iox_query_log_max_memory_count[5m]) +``` + +#### Cache performance + +```promql +# Cache hit rate +rate(influxdb3_parquet_cache_access_total{status="cached"}[5m]) / rate(influxdb3_parquet_cache_access_total[5m]) * 100 + +# Cache size in MB +influxdb3_parquet_cache_size_bytes / 1024 / 1024 +``` + +#### Object store throughput + +```promql +# Bytes per second to object store +rate(object_store_transfer_bytes_total[5m]) + +# Objects per second to object store +rate(object_store_transfer_objects_total[5m]) +``` +{{% /show-in %}} + +{{% show-in "enterprise" %}} +## Distributed monitoring setup + +### Prometheus configuration + +Configure Prometheus to scrape all cluster nodes: + +```yaml +# prometheus.yml +scrape_configs: + - job_name: 'influxdb3-enterprise' + static_configs: + - targets: + - 'ingester-01:8181' + - 'ingester-02:8181' + - 'query-01:8181' + - 'query-02:8181' + - 'compactor-01:8181' + - 'processor-01:8181' + metrics_path: '/metrics' + scrape_interval: 30s + relabel_configs: + - source_labels: [__address__] + target_label: node_name + regex: '([^:]+):.*' + replacement: '${1}' +``` + +### Grafana dashboards + +Create role-specific dashboards with the following suggested metrics for each dashboard: + +#### Cluster Overview Dashboard +- Node status and availability +- Request rates across all nodes +- Error rates by node and operation type +- Resource utilization summary + +#### Ingest Performance Dashboard +- Write throughput by ingest node +- Snapshot creation rates +- Memory usage and pressure +- WAL-to-Parquet conversion metrics + +#### Query Performance Dashboard +- Query latency percentiles by query node +- Cache hit rates and efficiency +- Inter-node coordination times +- Memory usage during query execution + +#### Operations Dashboard +- Compaction progress and performance +- Object store operation success rates +- Processing engine trigger rates +- System health indicators + +### Alerting for clusters + +Set up cluster-aware alerting rules: + +```yaml +# Prometheus alerting rules +groups: + - name: influxdb3_enterprise_cluster + rules: + - alert: NodeDown + expr: up{job="influxdb3-enterprise"} == 0 + for: 1m + labels: + severity: critical + annotations: + summary: "InfluxDB 3 Enterprise node {{ $labels.instance }} is down" + + - alert: HighCatalogConflicts + expr: rate(influxdb3_catalog_operation_retries_total[5m]) > 0.1 + for: 5m + labels: + severity: warning + annotations: + summary: "High catalog operation conflicts in cluster" + + - alert: UnbalancedLoad + expr: | + ( + stddev(sum(rate(http_requests_total[5m])) by (instance)) / + avg(sum(rate(http_requests_total[5m])) by (instance)) + ) > 0.5 + for: 10m + labels: + severity: info + annotations: + summary: "Unbalanced load distribution across cluster nodes" + + - alert: SlowInterNodeCommunication + expr: | + avg(rate(influxdb_iox_query_log_ingester_latency_to_full_data_seconds_sum[5m]) / + rate(influxdb_iox_query_log_ingester_latency_to_full_data_seconds_count[5m])) > 1.0 + for: 5m + labels: + severity: warning + annotations: + summary: "Slow inter-node communication detected" +``` + +### Node identification + +To enrich metrics with node identification, you can use either Telegraf or Prometheus-specific relabeling: + +#### Using Telegraf (platform-agnostic) + +Configure the Prometheus input plugin with processor plugins to add node identification: + +```toml +[[inputs.prometheus]] + urls = ["http://ingester-01:8181/metrics", "http://query-01:8181/metrics"] + + # Add node name from URL + [inputs.prometheus.tags] + node_name = "$1" + +[[processors.regex]] + # Extract node role from node name + [[processors.regex.tags]] + key = "node_name" + pattern = '^(ingester|query|compactor)-.*' + replacement = "${1}" + result_key = "node_role" + + # Simplify role names + [[processors.regex.tags]] + key = "node_role" + pattern = '^ingester$' + replacement = "ingest" + [[processors.regex.tags]] + key = "node_role" + pattern = '^compactor$' + replacement = "compact" +``` + +#### Using Prometheus relabeling + +If you use Prometheus to monitor your cluster, use relabeling to add node identification: + +```yaml +relabel_configs: + - source_labels: [__address__] + target_label: node_name + regex: '([^:]+):.*' + replacement: '${1}' + - source_labels: [node_name] + target_label: node_role + regex: 'ingester-.*' + replacement: 'ingest' + - source_labels: [node_name] + target_label: node_role + regex: 'query-.*' + replacement: 'query' + - source_labels: [node_name] + target_label: node_role + regex: 'compactor-.*' + replacement: 'compact' +``` +{{% /show-in %}} + +{{% show-in "core" %}} +## Integration with monitoring tools + +### Prometheus configuration + +Add {{< product-name >}} to your Prometheus configuration: + +```yaml +# prometheus.yml +scrape_configs: + - job_name: 'influxdb3-core' + static_configs: + - targets: ['localhost:8181'] + metrics_path: '/metrics' + scrape_interval: 30s +``` + +### Grafana dashboard + +Create dashboards with key metrics: + +1. **System Overview**: Request rates, error rates, memory usage +2. **Query Performance**: Query latency, throughput, memory per query +3. **Storage**: Object store operations, cache hit rates, file counts +4. **Runtime Health**: Task counts, worker utilization, panic rates + +### Alerting rules + +Set up alerts for critical conditions: + +```yaml +# Prometheus alerting rules +groups: + - name: influxdb3_core + rules: + - alert: HighErrorRate + expr: rate(http_requests_total{status=~"server_error"}[5m]) > 0.1 + for: 5m + labels: + severity: warning + annotations: + summary: "High error rate in InfluxDB 3 Core" + + - alert: HighQueryLatency + expr: histogram_quantile(0.95, rate(influxdb_iox_query_log_execute_duration_seconds_bucket[5m])) > 10 + for: 5m + labels: + severity: warning + annotations: + summary: "High query latency in InfluxDB 3 Core" + + - alert: LowCacheHitRate + expr: rate(influxdb3_parquet_cache_access_total{status="cached"}[5m]) / rate(influxdb3_parquet_cache_access_total[5m]) < 0.5 + for: 10m + labels: + severity: info + annotations: + summary: "Low cache hit rate in InfluxDB 3 Core" +``` +{{% /show-in %}} + +## Best practices + +### General monitoring practices + +1. **Monitor key metrics**: Focus on request rates, error rates, latency, and resource usage +2. **Set appropriate scrape intervals**: 15-30 seconds for most metrics +3. **Create meaningful alerts**: Alert on trends and thresholds that indicate real issues +4. **Use labels effectively**: Leverage metric labels for filtering and grouping +5. **Monitor long-term trends**: Track performance over time to identify patterns +6. **Correlate metrics**: Combine multiple metrics to understand system behavior + +{{% show-in "enterprise" %}} +### Cluster monitoring practices + +1. **Monitor each node type differently**: Focus on write metrics for ingest nodes, query metrics for query nodes +2. **Track load distribution**: Ensure work is balanced across nodes of the same type +3. **Monitor inter-node coordination**: Watch for communication delays between nodes +4. **Set up node-specific alerts**: Different thresholds for different node roles +5. **Use node labels**: Tag metrics with node roles and purposes +6. **Monitor shared resources**: Object store performance affects all nodes +7. **Track catalog conflicts**: High retry rates indicate coordination issues +8. **Regularly review dashboards and alerts**: Adjust as cluster usage patterns evolve +{{% /show-in %}} \ No newline at end of file diff --git a/content/shared/influxdb3-reference/metrics.md b/content/shared/influxdb3-reference/metrics.md new file mode 100644 index 0000000000..d48c89c0df --- /dev/null +++ b/content/shared/influxdb3-reference/metrics.md @@ -0,0 +1,581 @@ +InfluxDB exposes operational metrics in [Prometheus format](#prometheus-format) at the `/metrics` endpoint{{% show-in "enterprise" %}} on each cluster node{{% /show-in %}}. + +- [Access metrics](#access-metrics) +- [HTTP and gRPC metrics](#http-and-grpc-metrics) +- [Database operations](#database-operations) +- [Query performance](#query-performance) +- [Memory and caching](#memory-and-caching) +- [Object storage](#object-storage) +- [Runtime and system](#runtime-and-system) +{{% show-in "enterprise" %}} +- [Cluster-specific considerations](#cluster-specific-considerations) +{{% /show-in %}} +- [Prometheus format](#prometheus-format) + +## Access metrics + +{{% show-in "core" %}} +Metrics are available at `http://localhost:8181/metrics` by default. + +```bash +curl -s http://localhost:8181/metrics +``` +{{% /show-in %}} + +{{% show-in "enterprise" %}} +Metrics are available at `http://NODE_HOST:8181/metrics` on each cluster node. + +```bash +# Access metrics from specific nodes +curl -s http://ingester-01:8181/metrics +curl -s http://query-01:8181/metrics +curl -s http://compactor-01:8181/metrics +``` +{{% /show-in %}} + +## HTTP and gRPC metrics + +### http_requests_total +- **Type:** Counter +- **Description:** Total number of HTTP requests processed{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `method`: HTTP method (GET, POST, etc.) + - `method_path`: Method and path combination + - `path`: Request path + - `status`: Response status (ok, client_error, server_error, aborted, unexpected_response) + +{{% show-in "enterprise" %}} +**Cluster considerations:** Track per-node to monitor load distribution +{{% /show-in %}} + +``` +http_requests_total{method="POST",method_path="POST /api/v3/write_lp",path="/api/v3/write_lp",status="ok"} 1 +``` + +### http_request_duration_seconds +- **Type:** Histogram +- **Description:** Distribution of HTTP request latencies{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** Same as http_requests_total + +{{% show-in "enterprise" %}} +**Cluster considerations:** Compare latencies across nodes to identify performance bottlenecks +{{% /show-in %}} + +### http_response_body_size_bytes +- **Type:** Histogram +- **Description:** Distribution of HTTP response body sizes{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** Same as http_requests_total + +### grpc_requests_total +- **Type:** Counter +- **Description:** Total number of gRPC requests processed{{% show-in "enterprise" %}} (includes inter-node communication){{% /show-in %}} +- **Labels:** + - `path`: gRPC method path + - `status`: Response status + +{{% show-in "enterprise" %}} +**Cluster considerations:** High gRPC volumes indicate active inter-node communication +{{% /show-in %}} + +### grpc_request_duration_seconds +- **Type:** Histogram +- **Description:** Distribution of gRPC request latencies{{% show-in "enterprise" %}} (includes inter-node communication){{% /show-in %}} +- **Labels:** Same as grpc_requests_total + +{{% show-in "enterprise" %}} +**Cluster considerations:** Monitor for network latency between cluster nodes +{{% /show-in %}} + +### grpc_response_body_size_bytes +- **Type:** Histogram +- **Description:** Distribution of gRPC response body sizes +- **Labels:** Same as grpc_requests_total + +## Database operations + +### influxdb3_catalog_operations_total +- **Type:** Counter +- **Description:** Total catalog operations by type{{% show-in "enterprise" %}} across the cluster{{% /show-in %}} +- **Labels:** + - `type`: Operation type (create_database, create_admin_token, register_node, etc.) + +{{% show-in "enterprise" %}} +**Cluster considerations:** Monitor for catalog coordination across nodes +{{% /show-in %}} + +``` +influxdb3_catalog_operations_total{type="create_database"} 5 +influxdb3_catalog_operations_total{type="create_admin_token"} 2 +{{% show-in "enterprise" %}} +influxdb3_catalog_operations_total{type="register_node"} 6 +{{% /show-in %}} +``` + +### influxdb3_catalog_operation_retries_total +- **Type:** Counter +- **Description:** Catalog updates that had to be retried due to conflicts{{% show-in "enterprise" %}} between nodes{{% /show-in %}} +- **Labels:** None + +{{% show-in "enterprise" %}} +**Cluster considerations:** High retry rates indicate coordination issues or high contention +{{% /show-in %}} + +## Query performance + +### influxdb_iox_query_log_compute_duration_seconds +- **Type:** Histogram +- **Description:** CPU duration spent for query computation{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +{{% show-in "enterprise" %}} +**Cluster considerations:** Compare compute times across query nodes +{{% /show-in %}} + +### influxdb_iox_query_log_execute_duration_seconds +- **Type:** Histogram +- **Description:** Total time to execute queries{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +{{% show-in "enterprise" %}} +**Cluster considerations:** Track query performance across different query nodes +{{% /show-in %}} + +### influxdb_iox_query_log_plan_duration_seconds +- **Type:** Histogram +- **Description:** Time spent planning queries{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### influxdb_iox_query_log_end2end_duration_seconds +- **Type:** Histogram +- **Description:** Complete query duration from issue time to completion{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### influxdb_iox_query_log_permit_duration_seconds +- **Type:** Histogram +- **Description:** Time to acquire a semaphore permit for query execution{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### influxdb_iox_query_log_max_memory +- **Type:** Histogram +- **Description:** Peak memory allocated for processing queries{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### influxdb_iox_query_log_parquet_files +- **Type:** Histogram +- **Description:** Number of Parquet files processed by queries{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### influxdb_iox_query_log_partitions +- **Type:** Histogram +- **Description:** Number of partitions processed by queries{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### influxdb_iox_query_log_deduplicated_parquet_files +- **Type:** Histogram +- **Description:** Number of files held under a DeduplicateExec operator{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### influxdb_iox_query_log_deduplicated_partitions +- **Type:** Histogram +- **Description:** Number of partitions held under a DeduplicateExec operator{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### influxdb_iox_query_log_phase_current +- **Type:** Gauge +- **Description:** Number of queries currently in each execution phase{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `phase`: Query execution phase + +### influxdb_iox_query_log_phase_entered_total +- **Type:** Counter +- **Description:** Total number of queries that entered each execution phase{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `phase`: Query execution phase + +### influxdb_iox_query_log_ingester_latency_to_full_data_seconds +- **Type:** Histogram +- **Description:** Time from initial request until querier has all data from ingesters +- **Labels:** None + +{{% show-in "enterprise" %}} +**Cluster considerations:** Measures inter-node coordination efficiency in distributed queries +{{% /show-in %}} + +### influxdb_iox_query_log_ingester_latency_to_plan_seconds +- **Type:** Histogram +- **Description:** Time until querier can proceed with query planning +- **Labels:** None + +{{% show-in "enterprise" %}} +**Cluster considerations:** Indicates how quickly query nodes can coordinate with ingest nodes +{{% /show-in %}} + +### influxdb_iox_query_log_ingester_partition_count +- **Type:** Histogram +- **Description:** Number of ingester partitions involved in queries +- **Labels:** None + +{{% show-in "enterprise" %}} +**Cluster considerations:** Shows data distribution across ingest nodes +{{% /show-in %}} + +### influxdb_iox_query_log_ingester_response_rows +- **Type:** Histogram +- **Description:** Number of rows in ingester responses +- **Labels:** None + +### influxdb_iox_query_log_ingester_response_size +- **Type:** Histogram +- **Description:** Size of ingester record batches in bytes +- **Labels:** None + +{{% show-in "enterprise" %}} +**Cluster considerations:** Monitor network traffic between query and ingest nodes +{{% /show-in %}} + +### query_datafusion_query_execution_ooms_total +- **Type:** Counter +- **Description:** Number of out-of-memory errors encountered by the query engine{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +{{% show-in "enterprise" %}} +**Cluster considerations:** Track OOM events across query nodes to identify resource constraints +{{% /show-in %}} + +## Memory and caching + +### datafusion_mem_pool_bytes +- **Type:** Gauge +- **Description:** Number of bytes within the DataFusion memory pool{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +{{% show-in "enterprise" %}} +**Cluster considerations:** Monitor memory usage across different node types +{{% /show-in %}} + +### influxdb3_parquet_cache_access_total +- **Type:** Counter +- **Description:** Track accesses to the in-memory Parquet cache{{% show-in "enterprise" %}} per query node{{% /show-in %}} +- **Labels:** + - `status`: Access result (cached, miss, miss_while_fetching) + +{{% show-in "enterprise" %}} +**Cluster considerations:** Compare cache effectiveness across query nodes +{{% /show-in %}} + +``` +influxdb3_parquet_cache_access_total{status="cached"} 1500 +influxdb3_parquet_cache_access_total{status="miss"} 200 +``` + +### influxdb3_parquet_cache_size_bytes +- **Type:** Gauge +- **Description:** Current size of in-memory Parquet cache in bytes{{% show-in "enterprise" %}} per query node{{% /show-in %}} +- **Labels:** None + +### influxdb3_parquet_cache_size_number_of_files +- **Type:** Gauge +- **Description:** Number of files in the in-memory Parquet cache{{% show-in "enterprise" %}} per query node{{% /show-in %}} +- **Labels:** None + +### jemalloc_memstats_bytes +- **Type:** Gauge +- **Description:** Memory allocation statistics from jemalloc{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `type`: Memory statistic type (active, allocated, mapped, etc.) + +## Object storage + +### object_store_op_duration_seconds +- **Type:** Histogram +- **Description:** Duration of object store operations{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `op`: Operation type (get, put, delete, list, etc.) + - `result`: Operation result (success, error) + +{{% show-in "enterprise" %}} +**Cluster considerations:** All nodes access shared object store; monitor for hotspots +{{% /show-in %}} + +### object_store_op_headers_seconds +- **Type:** Histogram +- **Description:** Time to response headers for object store operations{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** Same as object_store_op_duration_seconds + +### object_store_op_ttfb_seconds +- **Type:** Histogram +- **Description:** Time to first byte for object store operations{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** Same as object_store_op_duration_seconds + +### object_store_transfer_bytes_total +- **Type:** Counter +- **Description:** Cumulative bytes transferred to/from object store{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `op`: Operation type (get, put) + +{{% show-in "enterprise" %}} +**Cluster considerations:** Ingest nodes show high 'put' activity; query nodes show high 'get' activity +{{% /show-in %}} + +### object_store_transfer_bytes_hist +- **Type:** Histogram +- **Description:** Distribution of bytes transferred to/from object store{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `op`: Operation type (get, put) + +### object_store_transfer_objects_total +- **Type:** Counter +- **Description:** Cumulative count of objects transferred to/from object store{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `op`: Operation type (get, put) + +### object_store_transfer_objects_hist +- **Type:** Histogram +- **Description:** Distribution of objects transferred to/from object store{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `op`: Operation type (get, put) + +## Runtime and system + +### process_start_time_seconds +- **Type:** Gauge +- **Description:** Start time of the process since Unix epoch{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### thread_panic_count_total +- **Type:** Counter +- **Description:** Number of thread panics observed{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### iox_async_semaphore_acquire_duration_seconds +- **Type:** Histogram +- **Description:** Duration to acquire async semaphore permits{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `semaphore`: Semaphore identifier + +### iox_async_semaphore_holders_acquired +- **Type:** Gauge +- **Description:** Number of currently acquired semaphore holders{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `semaphore`: Semaphore identifier + +### iox_async_semaphore_holders_cancelled_while_pending_total +- **Type:** Counter +- **Description:** Number of pending semaphore holders cancelled while waiting{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `semaphore`: Semaphore identifier + +### iox_async_semaphore_holders_pending +- **Type:** Gauge +- **Description:** Number of pending semaphore holders{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `semaphore`: Semaphore identifier + +### iox_async_semaphore_permits_acquired +- **Type:** Gauge +- **Description:** Number of currently acquired permits{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `semaphore`: Semaphore identifier + +### iox_async_semaphore_permits_cancelled_while_pending_total +- **Type:** Counter +- **Description:** Permits cancelled while waiting for semaphore{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `semaphore`: Semaphore identifier + +### iox_async_semaphore_permits_pending +- **Type:** Gauge +- **Description:** Number of pending permits{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `semaphore`: Semaphore identifier + +### iox_async_semaphore_permits_total +- **Type:** Gauge +- **Description:** Total number of permits in semaphore{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `semaphore`: Semaphore identifier + +### tokio_runtime_num_alive_tasks +- **Type:** Gauge +- **Description:** Current number of alive tasks in the Tokio runtime{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### tokio_runtime_blocking_queue_depth +- **Type:** Gauge +- **Description:** Number of tasks in the blocking thread pool queue{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### tokio_runtime_budget_forced_yield_count_total +- **Type:** Counter +- **Description:** Number of times tasks were forced to yield after exhausting budgets{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### tokio_runtime_global_queue_depth +- **Type:** Gauge +- **Description:** Number of tasks in the runtime's global queue{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### tokio_runtime_io_driver_ready_count_total +- **Type:** Counter +- **Description:** Number of ready events processed by the I/O driver{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### tokio_runtime_io_driver_fd_deregistered_count_total +- **Type:** Counter +- **Description:** Number of file descriptors deregistered by the I/O driver{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### tokio_runtime_io_driver_fd_registered_count_total +- **Type:** Counter +- **Description:** Number of file descriptors registered with the I/O driver{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### tokio_runtime_num_blocking_threads +- **Type:** Gauge +- **Description:** Number of additional threads spawned by the runtime{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### tokio_runtime_num_idle_blocking_threads +- **Type:** Gauge +- **Description:** Number of idle threads spawned for spawn_blocking calls{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### tokio_runtime_num_workers +- **Type:** Gauge +- **Description:** Number of worker threads used by the runtime{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### tokio_runtime_remote_schedule_count_total +- **Type:** Counter +- **Description:** Number of tasks scheduled from outside the runtime{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### tokio_worker_local_queue_depth +- **Type:** Gauge +- **Description:** Number of tasks in each worker's local queue{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `worker`: Worker thread identifier + +### tokio_worker_local_schedule_count_total +- **Type:** Counter +- **Description:** Tasks scheduled from within the runtime on local queues{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `worker`: Worker thread identifier + +### tokio_worker_mean_poll_time_seconds +- **Type:** Gauge +- **Description:** Exponentially weighted moving average of task poll duration{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `worker`: Worker thread identifier + +### tokio_worker_noop_count_total +- **Type:** Counter +- **Description:** Times worker threads unparked but performed no work{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `worker`: Worker thread identifier + +### tokio_worker_overflow_count_total +- **Type:** Counter +- **Description:** Times worker threads saturated their local queue{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `worker`: Worker thread identifier + +### tokio_worker_park_count_total +- **Type:** Counter +- **Description:** Total times worker threads have parked{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `worker`: Worker thread identifier + +### tokio_worker_poll_count_total +- **Type:** Counter +- **Description:** Number of tasks polled by worker threads{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `worker`: Worker thread identifier + +### tokio_worker_steal_count_total +- **Type:** Counter +- **Description:** Tasks stolen by worker threads from other workers{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `worker`: Worker thread identifier + +### tokio_worker_steal_operations_total +- **Type:** Counter +- **Description:** Number of steal operations performed by worker threads{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `worker`: Worker thread identifier + +### tokio_worker_total_busy_duration_seconds_total +- **Type:** Counter +- **Description:** Time worker threads have been busy{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** + - `worker`: Worker thread identifier + +### tokio_watchdog_hangs_total +- **Type:** Counter +- **Description:** Number of hangs detected by the Tokio watchdog{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +### tokio_watchdog_response_time_seconds +- **Type:** Histogram +- **Description:** Response time of the Tokio watchdog task{{% show-in "enterprise" %}} per node{{% /show-in %}} +- **Labels:** None + +{{% show-in "enterprise" %}} +## Cluster-specific considerations + +### Node identification + +For information on enriching metrics with node identification using Telegraf or Prometheus relabeling, see [Node identification in Monitor metrics](/influxdb3/enterprise/admin/monitor-metrics/#node-identification). + +### Key cluster metrics + +Focus on these metrics for cluster health: + +- **Load distribution**: `sum by (node_name) (rate(http_requests_total[5m]))` +- **Catalog conflicts**: `rate(influxdb3_catalog_operation_retries_total[5m])` +- **Inter-node latency**: `influxdb_iox_query_log_ingester_latency_to_full_data_seconds` +- **Node availability**: `up{job="influxdb3-enterprise"}` + +### Performance by node type + +Monitor different metrics based on [node specialization](/influxdb3/enterprise/admin/clustering/): + +- **Ingest nodes or all-in-one nodes handling writes**: + - `http_requests_total{path="/api/v3/write_lp"}` - Write operations via HTTP + - `grpc_requests_total{path="/api/v3/write_lp"}` - Write operations via gRPC + - `grpc_request_duration_seconds{path="/api/v3/write_lp"}` - Write operation latency + - `object_store_transfer_bytes_total{op="put"}` - Data written to object storage +- **Query nodes or all-in-one nodes handling queries**: + - `http_requests_total{path="/api/v3/query_sql"}` - SQL query requests + - `influxdb_iox_query_log_execute_duration_seconds` - Query execution time + - `influxdb3_parquet_cache_access_total` - Parquet cache performance +- **All nodes (configuration and management)**: + - `http_requests_total{path="/api/v3/configure/database"}` - Database configuration operations + - `http_requests_total{path="/api/v3/configure/token/admin"}` - Token management operations + - `influxdb3_catalog_operations_total` - Catalog operations (create_database, create_admin_token, register_node) +- **Compactor nodes or all-in-one nodes handling compaction**: + - `object_store_op_duration_seconds{op="put"}` - Compaction write performance + - `object_store_transfer_objects_total` - Files processed during compaction +{{% /show-in %}} + +## Prometheus format + +InfluxDB metrics are exposed in Prometheus exposition format, a text-based format that includes metric names, labels, and values. Each metric follows this structure: + +``` +metric_name{label1="value1",label2="value2"} metric_value timestamp +``` + +**Key characteristics:** +- **Metric names**: Use underscores and describe what is being measured +- **Labels**: Key-value pairs in curly braces that add dimensionality +- **Values**: Numeric measurements (integers or floats) +- **Timestamps**: Optional Unix timestamps (usually omitted for current time) + +**Metric types:** +- **Counter**: Cumulative values that only increase (for example, `http_requests_total`) +- **Gauge**: Values that can go up and down (for example, `tokio_runtime_num_alive_tasks`) +- **Histogram**: Samples observations and counts them in configurable buckets (for example, `http_request_duration_seconds`) + +For complete specification details, see the [Prometheus exposition format documentation](https://prometheus.io/docs/instrumenting/exposition_formats/). + From e8ccfb28c2d6bbf36c20647a376e404e982e6c3c Mon Sep 17 00:00:00 2001 From: Jason Stirnaman Date: Wed, 1 Oct 2025 16:14:16 -0500 Subject: [PATCH 3/9] test: Use custom influxdb3 image - Docker Hub doesn't have latest ARM64 --- compose.yaml | 49 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/compose.yaml b/compose.yaml index 612900f6a7..cd09712084 100644 --- a/compose.yaml +++ b/compose.yaml @@ -306,8 +306,8 @@ services: working_dir: /app influxdb3-core: container_name: influxdb3-core - image: influxdb:3-core - pull_policy: always + image: influxdb:3.5.0-core-arm64 + pull_policy: never # Set variables (except your auth token) for Core in the .env.3core file. env_file: - .env.3core @@ -338,8 +338,8 @@ services: - influxdb3-core-admin-token influxdb3-enterprise: container_name: influxdb3-enterprise - image: influxdb:3-enterprise - pull_policy: always + image: influxdb:3.5.0-enterprise-arm64 + pull_policy: never # Set license email and other variables (except your auth token) for Enterprise in the .env.3ent file. env_file: - .env.3ent @@ -499,7 +499,7 @@ services: remark-lint: container_name: remark-lint build: - context: . + context: . dockerfile: .ci/Dockerfile.remark profiles: - lint @@ -510,6 +510,39 @@ services: - type: bind source: ./CONTRIBUTING.md target: /app/CONTRIBUTING.md + prometheus: + container_name: prometheus + image: prom/prometheus:latest + ports: + - "9090:9090" + environment: + - INFLUXDB3_CORE_TOKEN=${INFLUXDB3_CORE_TOKEN} + - INFLUXDB3_ENTERPRISE_TOKEN=${INFLUXDB3_ENTERPRISE_TOKEN} + volumes: + - type: bind + source: ./test/influxdb3/prometheus.yml + target: /etc/prometheus/prometheus.yml + read_only: true + - type: volume + source: prometheus-data + target: /prometheus + entrypoint: + - /bin/sh + - -c + - | + echo "$$INFLUXDB3_CORE_TOKEN" > /tmp/core-token + echo "$$INFLUXDB3_ENTERPRISE_TOKEN" > /tmp/enterprise-token + exec /bin/prometheus \ + --config.file=/etc/prometheus/prometheus.yml \ + --storage.tsdb.path=/prometheus \ + --web.console.libraries=/usr/share/prometheus/console_libraries \ + --web.console.templates=/usr/share/prometheus/consoles \ + --web.enable-lifecycle + depends_on: + - influxdb3-core + - influxdb3-enterprise + profiles: + - monitoring volumes: test-content: cloud-tmp: @@ -517,4 +550,8 @@ volumes: cloud-serverless-tmp: clustered-tmp: telegraf-tmp: - v2-tmp: \ No newline at end of file + v2-tmp: + influxdb3-core-tmp: + influxdb2-data: + influxdb2-config: + prometheus-data: \ No newline at end of file From 3182f8bf6f60eeadb0d23fbba98917eb77ae1b15 Mon Sep 17 00:00:00 2001 From: Jason Stirnaman Date: Wed, 1 Oct 2025 16:15:41 -0500 Subject: [PATCH 4/9] test(influxdb3): Add metrics output and Prometheus tests for docs validation --- TESTING.md | 111 +++++ test/debug_metrics_test.py | 71 ++++ test/influxdb3/metrics_endpoint_test.py | 320 ++++++++++++++ test/influxdb3/prometheus.yml | 87 ++++ test/influxdb3/prometheus_integration_test.py | 391 ++++++++++++++++++ test/influxdb3/run-prometheus-tests.sh | 48 +++ test/pytest/pytest.ini | 4 +- test/run-metrics-tests.sh | 50 +++ test/show-metrics-sample.py | 88 ++++ 9 files changed, 1168 insertions(+), 2 deletions(-) create mode 100644 test/debug_metrics_test.py create mode 100644 test/influxdb3/metrics_endpoint_test.py create mode 100644 test/influxdb3/prometheus.yml create mode 100644 test/influxdb3/prometheus_integration_test.py create mode 100755 test/influxdb3/run-prometheus-tests.sh create mode 100755 test/run-metrics-tests.sh create mode 100644 test/show-metrics-sample.py diff --git a/TESTING.md b/TESTING.md index 233bb3a367..1405fb2242 100644 --- a/TESTING.md +++ b/TESTING.md @@ -121,6 +121,117 @@ Potential causes: # This is ignored ``` +### Metrics Endpoint Testing + +The metrics testing suite validates InfluxDB 3 Core and Enterprise metrics in two phases: + +1. **Phase 1: Direct metrics validation** - Validates metric format, existence, and types by directly querying InfluxDB endpoints +2. **Phase 2: Prometheus integration** - Validates Prometheus configuration, scraping, and relabeling work as documented + +#### Phase 1: Direct Metrics Validation + +The `test/influxdb3/metrics_endpoint_test.py` suite validates that InfluxDB 3 metrics endpoints expose all documented metrics in correct Prometheus format. + +**Basic Usage:** + +```bash +# Using the wrapper script (recommended) +./test/run-metrics-tests.sh + +# Direct execution with Docker Compose +docker compose run --rm influxdb3-core-pytest test/influxdb3/metrics_endpoint_test.py + +# Run specific test +docker compose run --rm influxdb3-core-pytest test/influxdb3/metrics_endpoint_test.py -k test_http_grpc_metrics +``` + +**Verbose Output:** + +Set `VERBOSE_METRICS_TEST=true` to see detailed output showing which metrics are searched and the actual matching lines from the Prometheus endpoint: + +```bash +# With wrapper script +VERBOSE_METRICS_TEST=true ./test/run-metrics-tests.sh + +# With Docker Compose +VERBOSE_METRICS_TEST=true docker compose run --rm \ + -e VERBOSE_METRICS_TEST \ + influxdb3-core-pytest \ + test/influxdb3/metrics_endpoint_test.py +``` + +Example verbose output: +``` +TEST: HTTP/gRPC Metrics +================================================================================ + +āœ“ Searching for: http_requests_total + Found 12 total occurrences + Matches: + # HELP http_requests_total accumulated total requests + # TYPE http_requests_total counter + http_requests_total{method="GET",path="/metrics",status="aborted"} 0 +``` + +#### Phase 2: Prometheus Integration Testing + +The `test/influxdb3/prometheus_integration_test.py` suite validates that Prometheus can scrape InfluxDB metrics and that the documented relabeling configuration works correctly. + +**What it validates:** +- Prometheus service discovers InfluxDB targets +- Scrape configuration works with authentication +- Relabeling adds `node_name` and `node_role` labels correctly +- Regex patterns in relabel_configs match documentation +- PromQL queries using relabeled metrics work +- Example queries from documentation execute successfully + +**Basic Usage:** + +```bash +# Using the wrapper script (recommended) +./test/run-metrics-tests.sh --prometheus + +# Run both direct and Prometheus tests +./test/run-metrics-tests.sh --all + +# Direct execution +./test/influxdb3/run-prometheus-tests.sh + +# With verbose output +VERBOSE_PROMETHEUS_TEST=true ./test/influxdb3/run-prometheus-tests.sh +``` + +**What happens during Prometheus tests:** + +1. Starts Prometheus service with documented configuration from `test/influxdb3/prometheus.yml` +2. Waits for Prometheus to discover and scrape both InfluxDB instances +3. Validates relabeling adds `node_name` label (extracted from `__address__`) +4. Validates relabeling adds `node_role` label (based on node name pattern) +5. Tests PromQL queries can filter by node labels +6. Validates example rate() and histogram_quantile() queries work + +**Prerequisites:** +- All Phase 1 prerequisites (see below) +- Prometheus service enabled with: `docker compose --profile monitoring up -d` + +#### Authentication + +Tests require authentication tokens for InfluxDB 3 instances. Store tokens in: +- `~/.env.influxdb3-core-admin-token` (for Core) +- `~/.env.influxdb3-enterprise-admin-token` (for Enterprise) + +Or set environment variables directly: +- `INFLUXDB3_CORE_TOKEN` +- `INFLUXDB3_ENTERPRISE_TOKEN` + +#### Prerequisites + +- Docker and Docker Compose installed +- Running InfluxDB 3 Core container (`influxdb3-core:8181`) +- Running InfluxDB 3 Enterprise container (`influxdb3-enterprise:8181`) +- Valid authentication tokens +- For Phase 2: Prometheus service (`docker compose --profile monitoring up -d`) + ## Link Validation with Link-Checker Link validation uses the `link-checker` tool to validate internal and external links in documentation files. diff --git a/test/debug_metrics_test.py b/test/debug_metrics_test.py new file mode 100644 index 0000000000..f5781e6aa5 --- /dev/null +++ b/test/debug_metrics_test.py @@ -0,0 +1,71 @@ +"""Debug test to show actual metrics output.""" + +import os +import requests + + +def test_show_actual_metrics(): + """Display actual metrics from Core instance.""" + + # Get token + token = os.environ.get("INFLUXDB3_CORE_TOKEN") + headers = {"Authorization": f"Token {token}"} if token else {} + + # Fetch metrics + url = "http://influxdb3-core:8181" + response = requests.get(f"{url}/metrics", headers=headers, timeout=5) + + print(f"\n{'='*80}") + print(f"ACTUAL METRICS FROM {url}") + print(f"Status Code: {response.status_code}") + print(f"Using Auth: {'Yes' if token else 'No'}") + print(f"{'='*80}\n") + + if response.status_code == 200: + lines = response.text.split('\n') + print(f"Total lines: {len(lines)}\n") + + # Show first 100 lines + print("First 100 lines of actual output:\n") + for i, line in enumerate(lines[:100], 1): + print(f"{i:4d} | {line}") + + # Show examples of documented metrics + print(f"\n{'='*80}") + print("SEARCHING FOR DOCUMENTED METRICS:") + print(f"{'='*80}\n") + + documented_metrics = [ + "http_requests_total", + "grpc_requests_total", + "influxdb3_catalog_operations_total", + "influxdb_iox_query_log_compute_duration_seconds", + "datafusion_mem_pool_bytes", + "object_store_op_duration_seconds", + "jemalloc_memstats_bytes", + ] + + for metric in documented_metrics: + # Find TYPE and HELP lines + type_line = next((line for line in lines if f"# TYPE {metric}" in line), None) + help_line = next((line for line in lines if f"# HELP {metric}" in line), None) + + # Find first few data lines + data_lines = [line for line in lines if line.startswith(metric) and not line.startswith("#")][:3] + + if type_line or help_line or data_lines: + print(f"\nāœ“ {metric}:") + if help_line: + print(f" {help_line}") + if type_line: + print(f" {type_line}") + for data in data_lines: + print(f" {data}") + else: + print(f"\nāœ— {metric}: NOT FOUND") + else: + print(f"ERROR: Status {response.status_code}") + print(response.text[:500]) + + # Always pass so we can see the output + assert True diff --git a/test/influxdb3/metrics_endpoint_test.py b/test/influxdb3/metrics_endpoint_test.py new file mode 100644 index 0000000000..40aabad5f1 --- /dev/null +++ b/test/influxdb3/metrics_endpoint_test.py @@ -0,0 +1,320 @@ +"""Test InfluxDB 3 metrics endpoint for PR #6422. + +This test suite validates that the metrics documentation in PR #6422 is accurate +by checking that all documented metrics are actually exposed by the +InfluxDB 3 Core and Enterprise instances. + +Usage: + # Basic test execution + docker compose run --rm influxdb3-core-pytest test/metrics_endpoint_test.py + + # With verbose output (shows actual metrics and matches) + VERBOSE_METRICS_TEST=true docker compose run --rm influxdb3-core-pytest test/metrics_endpoint_test.py + + # Using the wrapper script (recommended) + ./test/run-metrics-tests.sh + + # With verbose output using wrapper script + VERBOSE_METRICS_TEST=true ./test/run-metrics-tests.sh + +Verbose Output: + Set VERBOSE_METRICS_TEST=true to see detailed output showing: + - Which metrics are being searched for + - Actual matching lines from the Prometheus metrics endpoint + - Total occurrence counts (for tests that include comments) + - Clear indication when metrics are not found + + Example verbose output: + TEST: HTTP/gRPC Metrics + ================================================================================ + + āœ“ Searching for: http_requests_total + Found 12 total occurrences + Matches: + # HELP http_requests_total accumulated total requests + # TYPE http_requests_total counter + http_requests_total{method="GET",path="/metrics",status="aborted"} 0 + +Authentication: + These tests require authentication tokens for InfluxDB 3 Core and Enterprise. + If you get 401 errors, set the following environment variables: + - INFLUXDB3_CORE_TOKEN: Admin token for InfluxDB 3 Core instance + - INFLUXDB3_ENTERPRISE_TOKEN: Admin token for InfluxDB 3 Enterprise instance + +Prerequisites: + - Docker and Docker Compose installed + - Running InfluxDB 3 Core and Enterprise containers + - Valid authentication tokens stored in ~/.env.influxdb3-core-admin-token + and ~/.env.influxdb3-enterprise-admin-token (for wrapper script) +""" + +import os +import re + +import pytest +import requests + +# Set to True to see detailed output of what's being checked +VERBOSE_OUTPUT = os.environ.get("VERBOSE_METRICS_TEST", "false").lower() == "true" + + +class MetricsHelper: + """Helper class for metrics endpoint testing.""" + + @staticmethod + def get_auth_headers(token_env_var): + """Get authorization headers if token is set.""" + token = os.environ.get(token_env_var) + if token: + return {"Authorization": f"Token {token}"} + return {} + + @staticmethod + def get_metrics(url, token_env_var): + """Get metrics from endpoint with optional authentication.""" + headers = MetricsHelper.get_auth_headers(token_env_var) + response = requests.get(f"{url}/metrics", headers=headers, timeout=5) + + if response.status_code == 401: + pytest.skip(f"Authentication required. Set {token_env_var} environment variable.") + + assert response.status_code == 200, f"Metrics returned {response.status_code}" + return response.text + + @staticmethod + def print_metric_search(test_name, metrics, text, include_comments=False): + """Print verbose output showing searched metrics and matches.""" + if not VERBOSE_OUTPUT: + return + + print("\n" + "="*80) + print(f"TEST: {test_name}") + print("="*80) + + for metric in metrics: + lines = text.split('\n') + if include_comments: + matches = [line for line in lines if metric in line][:3] + else: + matches = [line for line in lines if metric in line and not line.startswith("#")][:3] + + print(f"\nāœ“ Searching for: {metric}") + if matches: + if include_comments: + print(f" Found {len([l for l in lines if metric in l])} total occurrences") + print(" Matches:") + for match in matches: + print(f" {match}") + else: + print(f" āœ— NOT FOUND") + + +def test_core_metrics_endpoint_accessible(): + """Test that Core metrics endpoint is accessible.""" + text = MetricsHelper.get_metrics("http://influxdb3-core:8181", "INFLUXDB3_CORE_TOKEN") + assert len(text) > 0, "Core metrics response is empty" + + +def test_enterprise_metrics_endpoint_accessible(): + """Test that Enterprise metrics endpoint is accessible.""" + text = MetricsHelper.get_metrics("http://influxdb3-enterprise:8181", "INFLUXDB3_ENTERPRISE_TOKEN") + assert len(text) > 0, "Enterprise metrics response is empty" + + +def test_prometheus_format(): + """Test that metrics follow Prometheus exposition format.""" + text = MetricsHelper.get_metrics("http://influxdb3-core:8181", "INFLUXDB3_CORE_TOKEN") + + # Check for HELP comments + assert "# HELP" in text, "Missing HELP comments" + + # Check for TYPE comments + assert "# TYPE" in text, "Missing TYPE comments" + + # Check for valid metric lines (name{labels} value or name value) + metric_pattern = r"^[a-zA-Z_][a-zA-Z0-9_]*(\{[^}]*\})?\s+[\d\.\+\-eE]+(\s+\d+)?$" + lines = [line for line in text.split("\n") if line and not line.startswith("#")] + assert any( + re.match(metric_pattern, line) for line in lines + ), "No valid metric lines found" + + +def test_http_grpc_metrics(): + """Test HTTP and gRPC metrics exist.""" + text = MetricsHelper.get_metrics("http://influxdb3-core:8181", "INFLUXDB3_CORE_TOKEN") + + metrics = [ + "http_requests_total", + "http_request_duration_seconds", + "http_response_body_size_bytes", + "grpc_requests_total", + "grpc_request_duration_seconds", + ] + + MetricsHelper.print_metric_search("HTTP/gRPC Metrics", metrics, text, include_comments=True) + + missing = [m for m in metrics if m not in text] + assert not missing, f"Missing HTTP/gRPC metrics: {missing}" + + +def test_database_operation_metrics(): + """Test database operation metrics exist.""" + text = MetricsHelper.get_metrics("http://influxdb3-core:8181", "INFLUXDB3_CORE_TOKEN") + + metrics = [ + "influxdb3_catalog_operations_total", + "influxdb3_catalog_operation_retries_total", + ] + + MetricsHelper.print_metric_search("Database Operation Metrics", metrics, text) + + missing = [m for m in metrics if m not in text] + assert not missing, f"Missing database operation metrics: {missing}" + + +def test_query_performance_metrics(): + """Test query performance metrics exist.""" + text = MetricsHelper.get_metrics("http://influxdb3-core:8181", "INFLUXDB3_CORE_TOKEN") + + metrics = [ + "influxdb_iox_query_log_compute_duration_seconds", + "influxdb_iox_query_log_execute_duration_seconds", + "influxdb_iox_query_log_plan_duration_seconds", + "influxdb_iox_query_log_end2end_duration_seconds", + "influxdb_iox_query_log_max_memory", + "influxdb_iox_query_log_parquet_files", + ] + + MetricsHelper.print_metric_search("Query Performance Metrics", metrics, text) + + missing = [m for m in metrics if m not in text] + assert not missing, f"Missing query performance metrics: {missing}" + + +def test_memory_caching_metrics(): + """Test memory and caching metrics exist.""" + text = MetricsHelper.get_metrics("http://influxdb3-core:8181", "INFLUXDB3_CORE_TOKEN") + + metrics = [ + "datafusion_mem_pool_bytes", + "influxdb3_parquet_cache_access_total", + "influxdb3_parquet_cache_size_bytes", + "influxdb3_parquet_cache_size_number_of_files", + "jemalloc_memstats_bytes", + ] + + MetricsHelper.print_metric_search("Memory & Caching Metrics", metrics, text) + + missing = [m for m in metrics if m not in text] + assert not missing, f"Missing memory/caching metrics: {missing}" + + +def test_object_storage_metrics(): + """Test object storage metrics exist.""" + text = MetricsHelper.get_metrics("http://influxdb3-core:8181", "INFLUXDB3_CORE_TOKEN") + + metrics = [ + "object_store_op_duration_seconds", + "object_store_transfer_bytes_total", + "object_store_transfer_objects_total", + ] + + MetricsHelper.print_metric_search("Object Storage Metrics", metrics, text) + + missing = [m for m in metrics if m not in text] + assert not missing, f"Missing object storage metrics: {missing}" + + +def test_runtime_system_metrics(): + """Test runtime and system metrics exist.""" + text = MetricsHelper.get_metrics("http://influxdb3-core:8181", "INFLUXDB3_CORE_TOKEN") + + metrics = [ + "process_start_time_seconds", + "thread_panic_count_total", + "tokio_runtime_num_alive_tasks", + ] + + MetricsHelper.print_metric_search("Runtime & System Metrics", metrics, text) + + missing = [m for m in metrics if m not in text] + assert not missing, f"Missing runtime/system metrics: {missing}" + + +def test_metric_types(): + """Test that key metrics have correct types.""" + text = MetricsHelper.get_metrics("http://influxdb3-core:8181", "INFLUXDB3_CORE_TOKEN") + + # Check for expected types (case-insensitive partial match) + type_checks = [ + ("http_requests_total", "counter"), + ("http_request_duration_seconds", "histogram"), + ("datafusion_mem_pool_bytes", "gauge"), + ] + + if VERBOSE_OUTPUT: + print("\n" + "="*80) + print("TEST: Metric Type Validation") + print("="*80) + for metric_name, expected_type in type_checks: + type_pattern = rf"# TYPE {metric_name}\s+{expected_type}" + match = re.search(type_pattern, text, re.IGNORECASE) + print(f"\nāœ“ Checking: {metric_name} should be {expected_type}") + if match: + print(f" Match: {match.group()}") + else: + print(f" āœ— NOT FOUND or WRONG TYPE") + + for metric_name, expected_type in type_checks: + # Look for TYPE line for this metric + type_pattern = rf"# TYPE {metric_name}\s+{expected_type}" + assert re.search( + type_pattern, text, re.IGNORECASE + ), f"Metric {metric_name} should be type {expected_type}" + + +def test_enterprise_cluster_metrics(): + """Test Enterprise-specific cluster metrics exist.""" + text = MetricsHelper.get_metrics("http://influxdb3-enterprise:8181", "INFLUXDB3_ENTERPRISE_TOKEN") + + # These metrics are mentioned in Enterprise documentation + metrics = [ + "influxdb3_catalog_operation_retries_total", + "influxdb_iox_query_log_ingester_latency", + ] + + MetricsHelper.print_metric_search("Enterprise Cluster Metrics", metrics, text) + + missing = [m for m in metrics if m not in text] + assert not missing, f"Missing Enterprise cluster metrics: {missing}" + + +@pytest.mark.parametrize("url,token_env,instance", [ + ("http://influxdb3-core:8181", "INFLUXDB3_CORE_TOKEN", "Core"), + ("http://influxdb3-enterprise:8181", "INFLUXDB3_ENTERPRISE_TOKEN", "Enterprise") +]) +def test_metrics_have_labels(url, token_env, instance): + """Test that metrics have proper labels.""" + text = MetricsHelper.get_metrics(url, token_env) + + # Find a metric with labels (look for http_requests_total) + label_pattern = r'http_requests_total\{[^}]+\}' + matches = re.findall(label_pattern, text) + + if VERBOSE_OUTPUT: + print("\n" + "="*80) + print(f"TEST: Metric Label Validation ({instance})") + print("="*80) + print(f"\nāœ“ Searching for labeled metrics using pattern: {label_pattern}") + print(f" Found {len(matches)} labeled metrics") + if matches: + print(" Sample matches:") + for match in matches[:3]: + print(f" {match}") + + assert len(matches) > 0, f"{instance}: No metrics with labels found" + + # Check that labels are properly formatted + for match in matches: + assert '="' in match, f"{instance}: Labels should use = and quotes" + assert match.endswith("}"), f"{instance}: Labels should end with }}" diff --git a/test/influxdb3/prometheus.yml b/test/influxdb3/prometheus.yml new file mode 100644 index 0000000000..38056c6630 --- /dev/null +++ b/test/influxdb3/prometheus.yml @@ -0,0 +1,87 @@ +# Prometheus configuration for testing InfluxDB 3 metrics +# Based on documentation in content/shared/influxdb3-admin/monitor-metrics.md +# This configuration matches the examples provided in PR #6422 + +# NOTE: If your InfluxDB instance requires authentication for the /metrics endpoint, +# you'll need to configure bearer_token or bearer_token_file in the scrape configs below. +# See: https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config + +global: + scrape_interval: 30s + evaluation_interval: 30s + external_labels: + monitor: 'influxdb3-test' + +# Scrape configurations +scrape_configs: + # InfluxDB 3 Core + # Documentation reference: lines 563-571 in monitor-metrics.md + - job_name: 'influxdb3-core' + static_configs: + - targets: ['influxdb3-core:8181'] + labels: + environment: 'test' + product: 'core' + metrics_path: '/metrics' + scrape_interval: 30s + scrape_timeout: 10s + # Authentication - uses credential file + # Token is written to /tmp/core-token by docker-compose entrypoint + authorization: + credentials_file: /tmp/core-token + # Fallback protocol for targets that don't send Content-Type + fallback_scrape_protocol: 'PrometheusText0.0.4' + + # Relabeling to add node identification (same as Enterprise) + relabel_configs: + # Extract node name from address + - source_labels: [__address__] + target_label: node_name + regex: '([^:]+):.*' + replacement: '${1}' + # Add node role based on name pattern + - source_labels: [node_name] + target_label: node_role + regex: '.*core.*' + replacement: 'all-in-one-core' + - source_labels: [node_name] + target_label: node_role + regex: '.*enterprise.*' + replacement: 'all-in-one-enterprise' + + # InfluxDB 3 Enterprise + # Documentation reference: lines 399-418 in monitor-metrics.md + # Includes relabeling from lines 536-553 + - job_name: 'influxdb3-enterprise' + static_configs: + - targets: ['influxdb3-enterprise:8181'] + labels: + environment: 'test' + product: 'enterprise' + metrics_path: '/metrics' + scrape_interval: 30s + scrape_timeout: 10s + # Authentication - uses credential file + # Token is written to /tmp/enterprise-token by docker-compose entrypoint + authorization: + credentials_file: /tmp/enterprise-token + # Fallback protocol for targets that don't send Content-Type + fallback_scrape_protocol: 'PrometheusText0.0.4' + + # Relabeling to add node identification + # Documentation reference: lines 536-553 + relabel_configs: + # Extract node name from address + - source_labels: [__address__] + target_label: node_name + regex: '([^:]+):.*' + replacement: '${1}' + # Add node role based on name pattern + - source_labels: [node_name] + target_label: node_role + regex: '.*core.*' + replacement: 'all-in-one-core' + - source_labels: [node_name] + target_label: node_role + regex: '.*enterprise.*' + replacement: 'all-in-one-enterprise' diff --git a/test/influxdb3/prometheus_integration_test.py b/test/influxdb3/prometheus_integration_test.py new file mode 100644 index 0000000000..6057995f86 --- /dev/null +++ b/test/influxdb3/prometheus_integration_test.py @@ -0,0 +1,391 @@ +"""Test Prometheus integration and relabeling for PR #6422. + +This test suite validates that the Prometheus configuration and relabeling +examples documented in PR #6422 actually work correctly. + +Unlike metrics_endpoint_test.py which directly queries InfluxDB endpoints, +this test: +1. Starts Prometheus with the documented configuration +2. Validates Prometheus can scrape InfluxDB endpoints +3. Verifies relabeling rules add node_name and node_role labels +4. Tests PromQL queries with the relabeled metrics + +Usage: + # Start Prometheus and run integration tests + docker compose --profile monitoring up -d + docker compose run --rm influxdb3-core-pytest test/prometheus_integration_test.py + + # Or use the wrapper script + ./test/run-prometheus-tests.sh + +Prerequisites: + - Docker and Docker Compose installed + - Running InfluxDB 3 Core and Enterprise containers + - Prometheus service started with --profile monitoring + - Valid authentication tokens (if required) +""" + +import os +import time + +import pytest +import requests + +# Prometheus API endpoint +PROMETHEUS_URL = os.environ.get("PROMETHEUS_URL", "http://prometheus:9090") + +# Set to True to see detailed output +VERBOSE_OUTPUT = os.environ.get("VERBOSE_PROMETHEUS_TEST", "false").lower() == "true" + + +class PrometheusHelper: + """Helper class for Prometheus integration testing.""" + + @staticmethod + def wait_for_prometheus(timeout=30): + """Wait for Prometheus to be ready.""" + start_time = time.time() + while time.time() - start_time < timeout: + try: + response = requests.get(f"{PROMETHEUS_URL}/-/ready", timeout=5) + if response.status_code == 200: + return True + except requests.exceptions.RequestException: + pass + time.sleep(1) + return False + + @staticmethod + def wait_for_targets(timeout=60): + """Wait for Prometheus to discover and scrape targets.""" + start_time = time.time() + while time.time() - start_time < timeout: + try: + response = requests.get( + f"{PROMETHEUS_URL}/api/v1/targets", + timeout=5 + ) + if response.status_code == 200: + data = response.json() + active_targets = data.get("data", {}).get("activeTargets", []) + + # Check if all targets are up + all_up = all( + target.get("health") == "up" + for target in active_targets + ) + + if all_up and len(active_targets) >= 2: + if VERBOSE_OUTPUT: + print(f"\nāœ“ All {len(active_targets)} targets are up") + return True + + if VERBOSE_OUTPUT: + up_count = sum( + 1 for t in active_targets + if t.get("health") == "up" + ) + print(f" Waiting for targets: {up_count}/{len(active_targets)} up") + except requests.exceptions.RequestException as e: + if VERBOSE_OUTPUT: + print(f" Error checking targets: {e}") + time.sleep(2) + return False + + @staticmethod + def query_prometheus(query): + """Execute a PromQL query.""" + response = requests.get( + f"{PROMETHEUS_URL}/api/v1/query", + params={"query": query}, + timeout=10 + ) + assert response.status_code == 200, f"Query failed: {response.text}" + return response.json() + + @staticmethod + def print_query_result(query, result): + """Print verbose query result.""" + if not VERBOSE_OUTPUT: + return + + print(f"\nāœ“ Query: {query}") + data = result.get("data", {}) + result_type = data.get("resultType") + results = data.get("result", []) + + print(f" Result type: {result_type}") + print(f" Number of results: {len(results)}") + + if results: + print(" Sample results:") + for result in results[:3]: + metric = result.get("metric", {}) + value = result.get("value", [None, None]) + print(f" {metric} => {value[1]}") + + +def test_prometheus_is_ready(): + """Test that Prometheus service is ready.""" + assert PrometheusHelper.wait_for_prometheus(), ( + "Prometheus not ready after 30 seconds. " + "Ensure Prometheus is running: docker compose --profile monitoring up -d" + ) + + +def test_prometheus_targets_discovered(): + """Test that Prometheus has discovered InfluxDB targets.""" + response = requests.get(f"{PROMETHEUS_URL}/api/v1/targets", timeout=10) + assert response.status_code == 200, "Failed to get targets" + + data = response.json() + targets = data.get("data", {}).get("activeTargets", []) + + if VERBOSE_OUTPUT: + print("\n" + "="*80) + print("TEST: Prometheus Target Discovery") + print("="*80) + for target in targets: + health = target.get("health") + job = target.get("labels", {}).get("job") + address = target.get("scrapeUrl") + print(f"\nāœ“ Target: {job}") + print(f" Health: {health}") + print(f" Address: {address}") + + # Should have at least 2 targets (core and enterprise) + assert len(targets) >= 2, f"Expected at least 2 targets, found {len(targets)}" + + # Check for expected job names + job_names = {target.get("labels", {}).get("job") for target in targets} + assert "influxdb3-core" in job_names, "Missing influxdb3-core target" + assert "influxdb3-enterprise" in job_names, "Missing influxdb3-enterprise target" + + +def test_prometheus_targets_up(): + """Test that all Prometheus targets are healthy.""" + assert PrometheusHelper.wait_for_targets(), ( + "Targets not healthy after 60 seconds. " + "Check that InfluxDB instances are running and accessible." + ) + + response = requests.get(f"{PROMETHEUS_URL}/api/v1/targets", timeout=10) + data = response.json() + targets = data.get("data", {}).get("activeTargets", []) + + unhealthy = [ + target for target in targets + if target.get("health") != "up" + ] + + assert not unhealthy, ( + f"Found {len(unhealthy)} unhealthy targets: " + f"{[t.get('labels', {}).get('job') for t in unhealthy]}" + ) + + +def test_relabeling_adds_node_name(): + """Test that relabeling adds node_name label. + + Documentation reference: monitor-metrics.md lines 536-540 + Relabeling extracts hostname from __address__ and adds as node_name. + """ + # Wait for metrics to be scraped + time.sleep(5) + + # Query for any metric with node_name label + query = 'http_requests_total{node_name!=""}' + result = PrometheusHelper.query_prometheus(query) + + PrometheusHelper.print_query_result(query, result) + + data = result.get("data", {}) + results = data.get("result", []) + + assert len(results) > 0, ( + "No metrics found with node_name label. " + "Relabeling may not be working correctly." + ) + + # Verify node_name values match expected patterns + node_names = { + result.get("metric", {}).get("node_name") + for result in results + } + + if VERBOSE_OUTPUT: + print(f"\nāœ“ Found node_name labels: {node_names}") + + # Should have node names for both core and enterprise + assert any("core" in name for name in node_names), ( + "No node_name containing 'core' found" + ) + assert any("enterprise" in name for name in node_names), ( + "No node_name containing 'enterprise' found" + ) + + +def test_relabeling_adds_node_role(): + """Test that relabeling adds node_role label. + + Documentation reference: monitor-metrics.md lines 541-553 + Relabeling assigns node_role based on node_name pattern. + """ + # Wait for metrics to be scraped + time.sleep(5) + + # Query for metrics with node_role label + query = 'http_requests_total{node_role!=""}' + result = PrometheusHelper.query_prometheus(query) + + PrometheusHelper.print_query_result(query, result) + + data = result.get("data", {}) + results = data.get("result", []) + + assert len(results) > 0, ( + "No metrics found with node_role label. " + "Relabeling may not be working correctly." + ) + + # Verify node_role values + node_roles = { + result.get("metric", {}).get("node_role") + for result in results + } + + if VERBOSE_OUTPUT: + print(f"\nāœ“ Found node_role labels: {node_roles}") + + # Based on test/prometheus.yml relabeling rules + expected_roles = {"all-in-one-core", "all-in-one-enterprise"} + assert node_roles & expected_roles, ( + f"Expected roles {expected_roles}, found {node_roles}" + ) + + +def test_query_metrics_by_node(): + """Test that metrics can be queried by node labels. + + This validates that users can filter metrics by node_name and node_role + as documented in the monitoring guide. + """ + # Wait for metrics to be scraped + time.sleep(5) + + # Query metrics for specific node + queries = [ + 'http_requests_total{node_name="influxdb3-core"}', + 'http_requests_total{node_name="influxdb3-enterprise"}', + 'http_requests_total{node_role="all-in-one-core"}', + 'http_requests_total{node_role="all-in-one-enterprise"}', + ] + + if VERBOSE_OUTPUT: + print("\n" + "="*80) + print("TEST: Query Metrics by Node Labels") + print("="*80) + + for query in queries: + result = PrometheusHelper.query_prometheus(query) + PrometheusHelper.print_query_result(query, result) + + data = result.get("data", {}) + results = data.get("result", []) + + assert len(results) > 0, f"No results for query: {query}" + + +def test_promql_rate_query(): + """Test rate() query from documentation examples. + + Documentation commonly shows rate queries for counters. + """ + # Wait for enough data + time.sleep(10) + + query = 'rate(http_requests_total[1m])' + result = PrometheusHelper.query_prometheus(query) + + PrometheusHelper.print_query_result(query, result) + + data = result.get("data", {}) + results = data.get("result", []) + + # Should have results (may be 0 if no recent requests) + assert isinstance(results, list), "Expected list of results" + + +def test_promql_histogram_quantile(): + """Test histogram_quantile() query from documentation examples. + + Documentation reference: Example queries for query duration metrics. + """ + # Wait for enough data + time.sleep(10) + + query = 'histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[1m]))' + result = PrometheusHelper.query_prometheus(query) + + PrometheusHelper.print_query_result(query, result) + + # Query should execute without error + assert result.get("status") == "success", ( + f"Query failed: {result.get('error')}" + ) + + +def test_enterprise_metrics_queryable(): + """Test that Enterprise-specific metrics are queryable via Prometheus.""" + # Wait for metrics to be scraped + time.sleep(5) + + # Query Enterprise-specific metrics + queries = [ + 'influxdb3_catalog_operation_retries_total', + 'influxdb_iox_query_log_ingester_latency', + ] + + if VERBOSE_OUTPUT: + print("\n" + "="*80) + print("TEST: Enterprise-Specific Metrics") + print("="*80) + + for query in queries: + result = PrometheusHelper.query_prometheus(query) + PrometheusHelper.print_query_result(query, result) + + # Query should execute (may have no results if no activity) + assert result.get("status") == "success", ( + f"Query failed: {result.get('error')}" + ) + + +def test_prometheus_config_matches_docs(): + """Verify Prometheus configuration matches documented examples. + + This test validates that test/prometheus.yml matches the configuration + examples in the documentation. + """ + response = requests.get(f"{PROMETHEUS_URL}/api/v1/status/config", timeout=10) + assert response.status_code == 200, "Failed to get Prometheus config" + + config = response.json() + config_yaml = config.get("data", {}).get("yaml", "") + + if VERBOSE_OUTPUT: + print("\n" + "="*80) + print("TEST: Prometheus Configuration") + print("="*80) + print("\nConfiguration (first 500 chars):") + print(config_yaml[:500]) + + # Verify key configuration elements from documentation + assert "influxdb3-core" in config_yaml, "Missing influxdb3-core job" + assert "influxdb3-enterprise" in config_yaml, "Missing influxdb3-enterprise job" + assert "relabel_configs" in config_yaml, "Missing relabel_configs" + assert "node_name" in config_yaml, "Missing node_name in relabeling" + assert "node_role" in config_yaml, "Missing node_role in relabeling" + + # Verify scrape settings + assert "/metrics" in config_yaml, "Missing /metrics path" diff --git a/test/influxdb3/run-prometheus-tests.sh b/test/influxdb3/run-prometheus-tests.sh new file mode 100755 index 0000000000..f5df654e06 --- /dev/null +++ b/test/influxdb3/run-prometheus-tests.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# Run Prometheus integration tests with authentication +# This script validates that Prometheus can scrape InfluxDB metrics +# and that relabeling configuration works as documented. + +set -e + +# Read tokens from secret files +INFLUXDB3_CORE_TOKEN=$(cat ~/.env.influxdb3-core-admin-token) +INFLUXDB3_ENTERPRISE_TOKEN=$(cat ~/.env.influxdb3-enterprise-admin-token) + +# Export for docker compose +export INFLUXDB3_CORE_TOKEN +export INFLUXDB3_ENTERPRISE_TOKEN +export VERBOSE_PROMETHEUS_TEST + +echo "Starting Prometheus integration tests..." +echo "" +echo "This will:" +echo " 1. Start Prometheus with documented configuration" +echo " 2. Wait for Prometheus to scrape InfluxDB endpoints" +echo " 3. Validate relabeling adds node_name and node_role labels" +echo " 4. Test PromQL queries with relabeled metrics" +echo "" + +# Start Prometheus if not already running +if ! docker ps | grep -q prometheus; then + echo "Starting Prometheus service..." + docker compose --profile monitoring up -d prometheus + echo "Waiting for Prometheus to start..." + sleep 5 +fi + +# Run tests +echo "Running Prometheus integration tests..." +docker compose run --rm \ + -e INFLUXDB3_CORE_TOKEN \ + -e INFLUXDB3_ENTERPRISE_TOKEN \ + -e VERBOSE_PROMETHEUS_TEST \ + -e PROMETHEUS_URL=http://prometheus:9090 \ + influxdb3-core-pytest \ + "test/influxdb3/prometheus_integration_test.py" "$@" + +echo "" +echo "Tests complete!" +echo "" +echo "To view Prometheus UI, visit: http://localhost:9090" +echo "To stop Prometheus: docker compose --profile monitoring down" diff --git a/test/pytest/pytest.ini b/test/pytest/pytest.ini index 2a8564cca2..4f785bff3f 100644 --- a/test/pytest/pytest.ini +++ b/test/pytest/pytest.ini @@ -5,8 +5,8 @@ python_files = *_test.py *_test_sh.py # Collect classes with names ending in Test. python_classes = *Test -# Collect all functions. -python_functions = * +# Collect test functions (exclude helpers). +python_functions = test_* filterwarnings = ignore::pytest.PytestReturnNotNoneWarning # Log settings. diff --git a/test/run-metrics-tests.sh b/test/run-metrics-tests.sh new file mode 100755 index 0000000000..d0a4017f5b --- /dev/null +++ b/test/run-metrics-tests.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Run metrics endpoint tests with authentication +# +# Usage: +# ./test/run-metrics-tests.sh # Run direct metrics tests +# ./test/run-metrics-tests.sh --prometheus # Run Prometheus integration tests +# ./test/run-metrics-tests.sh --all # Run both test suites + +set -e + +# Read tokens from secret files +INFLUXDB3_CORE_TOKEN=$(cat ~/.env.influxdb3-core-admin-token) +INFLUXDB3_ENTERPRISE_TOKEN=$(cat ~/.env.influxdb3-enterprise-admin-token) + +# Export for docker compose +export INFLUXDB3_CORE_TOKEN +export INFLUXDB3_ENTERPRISE_TOKEN +export VERBOSE_METRICS_TEST + +# Parse arguments +RUN_DIRECT=true +RUN_PROMETHEUS=false + +if [[ "$1" == "--prometheus" ]]; then + RUN_DIRECT=false + RUN_PROMETHEUS=true + shift +elif [[ "$1" == "--all" ]]; then + RUN_DIRECT=true + RUN_PROMETHEUS=true + shift +fi + +# Run direct metrics tests +if [[ "$RUN_DIRECT" == "true" ]]; then + echo "Running direct metrics endpoint tests..." + docker compose run --rm \ + -e INFLUXDB3_CORE_TOKEN \ + -e INFLUXDB3_ENTERPRISE_TOKEN \ + -e VERBOSE_METRICS_TEST \ + influxdb3-core-pytest \ + "test/influxdb3/metrics_endpoint_test.py" "$@" + echo "" +fi + +# Run Prometheus integration tests +if [[ "$RUN_PROMETHEUS" == "true" ]]; then + echo "Running Prometheus integration tests..." + ./test/influxdb3/run-prometheus-tests.sh "$@" +fi diff --git a/test/show-metrics-sample.py b/test/show-metrics-sample.py new file mode 100644 index 0000000000..acd1fec158 --- /dev/null +++ b/test/show-metrics-sample.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +"""Display sample metrics output from InfluxDB 3 instances.""" + +import os +import sys +import requests + +def show_metrics_sample(url, token_env_var, instance_name, num_lines=150): + """Fetch and display sample metrics.""" + print(f"\n{'='*80}") + print(f"{instance_name} Metrics Sample (first {num_lines} lines)") + print(f"URL: {url}/metrics") + print(f"{'='*80}\n") + + # Get auth headers + headers = {} + token = os.environ.get(token_env_var) + if token: + headers = {"Authorization": f"Token {token}"} + print(f"āœ“ Using authentication token from {token_env_var}\n") + else: + print(f"⚠ No token found in {token_env_var} - trying without auth\n") + + try: + response = requests.get(f"{url}/metrics", headers=headers, timeout=5) + + if response.status_code == 401: + print(f"āœ— Authentication required but no valid token provided") + return + + if response.status_code != 200: + print(f"āœ— Unexpected status code: {response.status_code}") + return + + # Display first N lines + lines = response.text.split('\n') + print(f"Total lines: {len(lines)}\n") + + for i, line in enumerate(lines[:num_lines], 1): + print(f"{i:4d} | {line}") + + if len(lines) > num_lines: + print(f"\n... ({len(lines) - num_lines} more lines)") + + # Show some interesting metrics + print(f"\n{'='*80}") + print("Sample Metric Searches:") + print(f"{'='*80}\n") + + metrics_to_show = [ + "http_requests_total", + "grpc_requests_total", + "influxdb3_catalog_operations_total", + "influxdb_iox_query_log_compute_duration_seconds", + "datafusion_mem_pool_bytes", + "object_store_op_duration_seconds", + ] + + for metric in metrics_to_show: + matching = [line for line in lines if metric in line and not line.startswith("#")] + if matching: + print(f"āœ“ Found '{metric}' - showing first 3 values:") + for match in matching[:3]: + print(f" {match}") + else: + print(f"āœ— Metric '{metric}' not found") + + except Exception as e: + print(f"āœ— Error fetching metrics: {e}") + +if __name__ == "__main__": + # Show Core metrics + show_metrics_sample( + "http://influxdb3-core:8181", + "INFLUXDB3_CORE_TOKEN", + "InfluxDB 3 Core", + num_lines=100 + ) + + print("\n\n") + + # Show Enterprise metrics + show_metrics_sample( + "http://influxdb3-enterprise:8181", + "INFLUXDB3_ENTERPRISE_TOKEN", + "InfluxDB 3 Enterprise", + num_lines=100 + ) From 8f62e5bfbfe5d5187e7b821c57ef58d2efca27ac Mon Sep 17 00:00:00 2001 From: Jason Stirnaman Date: Thu, 2 Oct 2025 10:26:49 -0500 Subject: [PATCH 5/9] docs(influxdb3): Add system:metrics:read token examples and clarify authentication - Add system:metrics:read token creation examples to Enterprise token docs - Document both CLI and HTTP API approaches for creating metrics tokens - Clarify that Enterprise supports both admin and fine-grained tokens for /metrics - Add node identification explanation to monitoring documentation Addresses PR #6422 review comments about authentication and token permissions. Test validation: All examples validated with InfluxDB 3.5.0 Enterprise. --- .../admin/tokens/resource/create.md | 48 +++++++++++++++++-- .../shared/influxdb3-admin/monitor-metrics.md | 26 ++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/content/influxdb3/enterprise/admin/tokens/resource/create.md b/content/influxdb3/enterprise/admin/tokens/resource/create.md index e7de10b617..ce58142235 100644 --- a/content/influxdb3/enterprise/admin/tokens/resource/create.md +++ b/content/influxdb3/enterprise/admin/tokens/resource/create.md @@ -402,12 +402,24 @@ In your terminal, run the `influxdb3 create token --permission` command and prov - `health`: The specific system resource to grant permissions to. - `read`: The permission to grant to the token (system tokens are always read-only). -{{% code-placeholders "System health token|1y" %}} +The following example shows how to create specific system tokens: + +{{% code-placeholders "(System [a-z]+ token|1y" %}} ```bash influxdb3 create token \ --permission "system:health:read" \ --name "System health token" \ --expiry 1y + +influxdb3 create token \ + --permission "system:metrics:read" \ + --name "System metrics token" \ + --expiry 1y + +influxdb3 create token \ + --permission "system:ping:read" \ + --name "System ping token" \ + --expiry 1y ``` {{% /code-placeholders %}} @@ -444,9 +456,9 @@ In the request body, provide the following parameters: - `permissions`: an array of token permission actions (only `"read"` for system tokens) - `expiry_secs`: Specify the token expiration time in seconds. -The following example shows how to use the HTTP API to create a system token: +The following example shows how to use the HTTP API to create specific system tokens: -{{% code-placeholders "AUTH_TOKEN|System health token|300000" %}} +{{% code-placeholders "AUTH_TOKEN|(System [a-z]+ token)|300000" %}} ```bash curl \ @@ -463,6 +475,36 @@ curl \ }], "expiry_secs": 300000 }' + +curl \ +"http://{{< influxdb/host >}}/api/v3/enterprise/configure/token" \ +--header 'Accept: application/json' \ +--header 'Content-Type: application/json' \ +--header "Authorization: Bearer AUTH_TOKEN" \ +--data '{ + "token_name": "System metrics token", + "permissions": [{ + "resource_type": "system", + "resource_identifier": ["metrics"], + "actions": ["read"] + }], + "expiry_secs": 300000 +}' + +curl \ +"http://{{< influxdb/host >}}/api/v3/enterprise/configure/token" \ +--header 'Accept: application/json' \ +--header 'Content-Type: application/json' \ +--header "Authorization: Bearer AUTH_TOKEN" \ +--data '{ + "token_name": "System ping token", + "permissions": [{ + "resource_type": "system", + "resource_identifier": ["ping"], + "actions": ["read"] + }], + "expiry_secs": 300000 +}' ``` {{% /code-placeholders %}} diff --git a/content/shared/influxdb3-admin/monitor-metrics.md b/content/shared/influxdb3-admin/monitor-metrics.md index ca1688960a..5ea2f183f2 100644 --- a/content/shared/influxdb3-admin/monitor-metrics.md +++ b/content/shared/influxdb3-admin/monitor-metrics.md @@ -26,6 +26,11 @@ curl -s -H "Authorization: Token AUTH_TOKEN" http://node:8181/metrics {{% show-in "enterprise" %}} ### View metrics from specific nodes +> [!Note] +> {{< product-name >}} supports two token types for the `/metrics` endpoint: +> - {{% token-link "Admin" %}}: Full access to all metrics +> - {{% token-link "Fine-grained" "resource/" %}} with `system:metrics:read` permission: Read-only access to metrics + ```bash { placeholders="AUTH_TOKEN" } # View metrics from specific nodes curl -s http://ingester-01:8181/metrics @@ -61,6 +66,27 @@ Metrics are exposed in [Prometheus exposition format](https://prometheus.io/docs metric_name{label1="value1",label2="value2"} 42.0 ``` +### Node identification in metrics + +{{< product-name >}} metrics don't include automatic node identification labels. +To identify which node produced each metric, you must configure your monitoring tool to add node labels during scraping. + +{{% show-in "enterprise" %}} +For multi-node clusters, node identification is essential for troubleshooting and monitoring individual node performance. +Many monitoring tools support adding static or dynamic labels during the scrape process (Prometheus calls this "relabeling")--for example: + +1. **Extract node hostname or IP** from the scrape target address +2. **Add extracted labels to metrics** during the scrape process + +| Hostname | Node Identification | Recommended? | +| -------------------------- | ------------------- | ----------------- | +| `ingester-01`, `query-02` | Extract role and ID | Yes | +| `node-01.cluster.internal` | Extract ID | Consider adding role information | +| `192.168.1.10` | IP address only | No, consider renaming with ID and role | + +For configuration examples, see [Add node identification with Prometheus](#add-node-identification-with-prometheus). +{{% /show-in %}} + ## Metric categories {{< product-name >}} exposes the following{{% show-in "enterprise" %}} base{{% /show-in %}} categories of metrics{{% show-in "enterprise" %}}, plus additional cluster-aware metrics{{% /show-in %}}: From a4485fc5dfc169e36085fcdfa34066a823220881 Mon Sep 17 00:00:00 2001 From: Jason Stirnaman Date: Thu, 2 Oct 2025 13:16:18 -0500 Subject: [PATCH 6/9] docs(influxdb3): Clarify metrics reporting across node modes Addresses @sanderson's comment: "Do irrelevant metrics still get reported? Do all nodes report the same metric, no matter what mode they're running in?" ## Changes ### Documentation Updates **content/shared/influxdb3-reference/metrics.md:** - Add "Metrics reporting across node modes" section under cluster considerations - Explain that all nodes report the same 120 metrics regardless of mode - Clarify differences appear in values/labels, not metric availability - Remove mention of HTTP/gRPC metrics appearing dynamically (less relevant) **content/shared/influxdb3-admin/monitor-metrics.md:** - Add Note callout in "Metric categories" section - Provide same clarifications in more prominent location - Simplify explanation for better readability ### Testing Configuration **compose.yaml:** - Add specialized Enterprise nodes for testing: - influxdb3-enterprise-write (mode: ingest, port 8183) - influxdb3-enterprise-query (mode: query, port 8184) - Fix port conflicts between specialized nodes - Enable validation of metrics behavior across node modes ## Test Results Validated with running Enterprise nodes in different modes: - All nodes expose same 120 unique metrics - Metrics not filtered by node specialization - Metric values reflect actual node activity - Confirmed standard Prometheus behavior See .context/issues/pr-6422-comment-responses.md for detailed test results. --- compose.yaml | 68 +++++++++++++++++++ .../shared/influxdb3-admin/monitor-metrics.md | 8 +++ content/shared/influxdb3-reference/metrics.md | 10 +++ 3 files changed, 86 insertions(+) diff --git a/compose.yaml b/compose.yaml index cd09712084..c35eb79497 100644 --- a/compose.yaml +++ b/compose.yaml @@ -369,6 +369,74 @@ services: target: /var/lib/influxdb3/plugins/custom secrets: - influxdb3-enterprise-admin-token + influxdb3-enterprise-write: + container_name: influxdb3-enterprise-write + image: influxdb:3.5.0-enterprise-arm64 + pull_policy: never + # Set license email and other variables (except your auth token) for Enterprise in the .env.3ent file. + env_file: + - .env.3ent + ports: + - 8183:8181 + command: + - influxdb3 + - serve + - --node-id=writer-0 + - --mode=ingest + - --cluster-id=cluster0 + - --object-store=file + - --data-dir=/var/lib/influxdb3/data + - --plugin-dir=/var/lib/influxdb3/plugins + - --log-filter=debug + - --verbose + environment: + - INFLUXDB3_AUTH_TOKEN=/run/secrets/influxdb3-enterprise-admin-token + volumes: + - type: bind + source: test/.influxdb3/enterprise/data + target: /var/lib/influxdb3/data + - type: bind + source: test/.influxdb3/plugins/influxdata + target: /var/lib/influxdb3/plugins + - type: bind + source: test/.influxdb3/enterprise/plugins + target: /var/lib/influxdb3/plugins/custom + secrets: + - influxdb3-enterprise-admin-token + influxdb3-enterprise-query: + container_name: influxdb3-enterprise-query + image: influxdb:3.5.0-enterprise-arm64 + pull_policy: never + # Set license email and other variables (except your auth token) for Enterprise in the .env.3ent file. + env_file: + - .env.3ent + ports: + - 8184:8181 + command: + - influxdb3 + - serve + - --node-id=querier-0 + - --mode=query + - --cluster-id=cluster0 + - --object-store=file + - --data-dir=/var/lib/influxdb3/data + - --plugin-dir=/var/lib/influxdb3/plugins + - --log-filter=debug + - --verbose + environment: + - INFLUXDB3_AUTH_TOKEN=/run/secrets/influxdb3-enterprise-admin-token + volumes: + - type: bind + source: test/.influxdb3/enterprise/data + target: /var/lib/influxdb3/data + - type: bind + source: test/.influxdb3/plugins/influxdata + target: /var/lib/influxdb3/plugins + - type: bind + source: test/.influxdb3/enterprise/plugins + target: /var/lib/influxdb3/plugins/custom + secrets: + - influxdb3-enterprise-admin-token telegraf-pytest: container_name: telegraf-pytest image: influxdata/docs-pytest diff --git a/content/shared/influxdb3-admin/monitor-metrics.md b/content/shared/influxdb3-admin/monitor-metrics.md index 5ea2f183f2..1f385196dc 100644 --- a/content/shared/influxdb3-admin/monitor-metrics.md +++ b/content/shared/influxdb3-admin/monitor-metrics.md @@ -91,6 +91,14 @@ For configuration examples, see [Add node identification with Prometheus](#add-n {{< product-name >}} exposes the following{{% show-in "enterprise" %}} base{{% /show-in %}} categories of metrics{{% show-in "enterprise" %}}, plus additional cluster-aware metrics{{% /show-in %}}: +{{% show-in "enterprise" %}} +> [!Note] +> #### Metrics reporting across node modes +> All nodes in an {{< product-name >}} cluster report the same set of metrics regardless of their configured [mode](/influxdb3/enterprise/reference/config-options/#mode) (ingest, query, compact, process, or all). +> The difference between nodes is in the metric _values_ and labels, which reflect the actual activity on each node. +> For example, an ingest-only node reports query-related metrics with minimal or zero values. +{{% /show-in %}} + ### HTTP and gRPC metrics Monitor API request patterns{{% show-in "enterprise" %}} across the cluster{{% /show-in %}}: diff --git a/content/shared/influxdb3-reference/metrics.md b/content/shared/influxdb3-reference/metrics.md index d48c89c0df..d9e8ab73b5 100644 --- a/content/shared/influxdb3-reference/metrics.md +++ b/content/shared/influxdb3-reference/metrics.md @@ -523,6 +523,16 @@ influxdb3_parquet_cache_access_total{status="miss"} 200 {{% show-in "enterprise" %}} ## Cluster-specific considerations +### Metrics reporting across node modes + +All nodes in an InfluxDB 3 Enterprise cluster report the same set of metrics regardless of their configured [mode](/influxdb3/enterprise/reference/config-options/#mode) (ingest, query, compact, process, or all). +Metrics are not filtered based on node specialization. +The difference between nodes is in the metric _values_ and labels, which reflect the actual activity on each node. + +For example: +- An ingest-only node reports query-related metrics, but with minimal or zero values +- A query-only node reports write-related metrics, but with minimal or zero values + ### Node identification For information on enriching metrics with node identification using Telegraf or Prometheus relabeling, see [Node identification in Monitor metrics](/influxdb3/enterprise/admin/monitor-metrics/#node-identification). From 3c2cbaccff63029b8518ccbaf8d1b07ea170ba3a Mon Sep 17 00:00:00 2001 From: Jason Stirnaman Date: Thu, 2 Oct 2025 14:43:58 -0500 Subject: [PATCH 7/9] docs(influxdb3): Prioritize Telegraf over Prometheus for metrics collection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses @sanderson's comment: "Why not suggest users use Telegraf to collect these metrics and store them in another InfluxDB instance rather than Prometheus?" ## Changes ### Enterprise Monitoring Setup **Before:** Prometheus configuration appeared first **After:** Telegraf configuration with "(recommended)" label appears first **New Telegraf section includes:** - Complete configuration with `outputs.influxdb_v3` for storing in monitoring instance - `inputs.prometheus` for scraping cluster node metrics - `processors.regex` for extracting node_name and node_role from URLs - Start commands for running Telegraf as a service - SQL query examples for analyzing collected metrics in InfluxDB **Prometheus section:** - Moved to "Alternative: Prometheus configuration" - Retained for users preferring Prometheus ecosystem - Includes separate "Add node identification with Prometheus" section ### Core Monitoring Setup **Before:** Only Prometheus configuration shown **After:** Telegraf appears first with "(recommended)" label **New sections:** - "Collect metrics with Telegraf (recommended)" with complete config - "Alternative: Prometheus configuration" for Prometheus users - SQL query examples for monitoring InfluxDB 3 Core metrics ## Benefits 1. **InfluxDB-native workflow**: Collect InfluxDB metrics → Store in InfluxDB → Query with SQL 2. **Consistent tooling**: Users already familiar with Telegraf for data collection 3. **SQL queries**: Natural fit for InfluxDB users vs learning PromQL 4. **Centralized monitoring**: Store metrics in separate InfluxDB instance 5. **Platform agnostic**: Telegraf runs anywhere without Prometheus infrastructure ## Documentation Coverage - āœ… Complete Telegraf configurations for both Core and Enterprise - āœ… Node identification through processor plugins - āœ… SQL query examples for common monitoring scenarios - āœ… Prometheus approach retained as alternative - āœ… Clear "(recommended)" and "Alternative" labels throughout Addresses PR #6422 comment 4. --- .../shared/influxdb3-admin/monitor-metrics.md | 239 ++++++++++++++---- 1 file changed, 186 insertions(+), 53 deletions(-) diff --git a/content/shared/influxdb3-admin/monitor-metrics.md b/content/shared/influxdb3-admin/monitor-metrics.md index 1f385196dc..1fa78ff496 100644 --- a/content/shared/influxdb3-admin/monitor-metrics.md +++ b/content/shared/influxdb3-admin/monitor-metrics.md @@ -426,9 +426,97 @@ rate(object_store_transfer_objects_total[5m]) {{% show-in "enterprise" %}} ## Distributed monitoring setup -### Prometheus configuration +### Collect metrics with Telegraf (recommended) -Configure Prometheus to scrape all cluster nodes: +Use Telegraf to collect metrics from all cluster nodes and store them in a separate {{< product-name >}} instance for centralized monitoring. + +#### Configure Telegraf + +Create a Telegraf configuration file (`telegraf.conf`) to scrape metrics from your cluster nodes: + +```toml +# Telegraf configuration for InfluxDB 3 Enterprise monitoring + +# Output to monitoring InfluxDB instance +[[outputs.influxdb_v3]] + urls = ["http://monitoring-influxdb:8181"] + database = "monitoring" + token = "MONITORING_AUTH_TOKEN" + +# Scrape metrics from cluster nodes +[[inputs.prometheus]] + urls = [ + "http://ingester-01:8181/metrics", + "http://ingester-02:8181/metrics", + "http://query-01:8181/metrics", + "http://query-02:8181/metrics", + "http://compactor-01:8181/metrics" + ] + metric_version = 2 + + # Add node identification from URL + [inputs.prometheus.tags] + cluster = "production" + +# Extract node name and role from URL +[[processors.regex]] + namepass = ["*"] + + [[processors.regex.tags]] + key = "url" + pattern = "^http://([^:]+):.*" + replacement = "${1}" + result_key = "node_name" + + [[processors.regex.tags]] + key = "node_name" + pattern = "^(ingester|query|compactor|processor)-.*" + replacement = "${1}" + result_key = "node_role" +``` + +Replace the following: + +- {{% code-placeholder-key %}}`MONITORING_AUTH_TOKEN`{{% /code-placeholder-key %}}: your {{% token-link %}} for the monitoring InfluxDB instance + +#### Start Telegraf + +```bash +# Start Telegraf with the configuration +telegraf --config telegraf.conf + +# Run as a service (systemd example) +sudo systemctl start telegraf +sudo systemctl enable telegraf +``` + +#### Query collected metrics + +Query the monitoring database using SQL: + +```sql +-- Request rate by node +SELECT + node_name, + node_role, + COUNT(*) as request_count +FROM http_requests_total +WHERE time >= now() - INTERVAL '5 minutes' +GROUP BY node_name, node_role +ORDER BY request_count DESC; + +-- Query latency percentiles by node +SELECT + node_name, + APPROX_PERCENTILE_CONT(value, 0.95) as p95_latency_seconds +FROM http_request_duration_seconds +WHERE time >= now() - INTERVAL '1 hour' +GROUP BY node_name; +``` + +### Alternative: Prometheus configuration + +If you prefer Prometheus, configure it to scrape all cluster nodes: ```yaml # prometheus.yml @@ -527,72 +615,117 @@ groups: summary: "Slow inter-node communication detected" ``` -### Node identification +### Add node identification with Prometheus -To enrich metrics with node identification, you can use either Telegraf or Prometheus-specific relabeling: +If using Prometheus instead of Telegraf, add node identification through relabeling: -#### Using Telegraf (platform-agnostic) +```yaml +# prometheus.yml +scrape_configs: + - job_name: 'influxdb3-enterprise' + static_configs: + - targets: + - 'ingester-01:8181' + - 'query-01:8181' + relabel_configs: + # Extract node name from address + - source_labels: [__address__] + target_label: node_name + regex: '([^:]+):.*' + replacement: '${1}' -Configure the Prometheus input plugin with processor plugins to add node identification: + # Assign node role based on hostname pattern + - source_labels: [node_name] + target_label: node_role + regex: 'ingester-.*' + replacement: 'ingest' + - source_labels: [node_name] + target_label: node_role + regex: 'query-.*' + replacement: 'query' + - source_labels: [node_name] + target_label: node_role + regex: 'compactor-.*' + replacement: 'compact' +``` -```toml -[[inputs.prometheus]] - urls = ["http://ingester-01:8181/metrics", "http://query-01:8181/metrics"] +Query metrics with node labels in PromQL: - # Add node name from URL - [inputs.prometheus.tags] - node_name = "$1" +```promql +# Request rate by node and role +sum(rate(http_requests_total[5m])) by (node_name, node_role) -[[processors.regex]] - # Extract node role from node name - [[processors.regex.tags]] - key = "node_name" - pattern = '^(ingester|query|compactor)-.*' - replacement = "${1}" - result_key = "node_role" +# Filter metrics for specific node role +http_requests_total{node_role="ingest"} +``` +{{% /show-in %}} - # Simplify role names - [[processors.regex.tags]] - key = "node_role" - pattern = '^ingester$' - replacement = "ingest" - [[processors.regex.tags]] - key = "node_role" - pattern = '^compactor$' - replacement = "compact" +{{% show-in "core" %}} +## Integration with monitoring tools + +### Collect metrics with Telegraf (recommended) + +Use Telegraf to collect metrics and store them in a separate {{< product-name >}} instance for monitoring. + +#### Configure Telegraf + +Create a Telegraf configuration file (`telegraf.conf`): + +```toml +# Telegraf configuration for InfluxDB 3 Core monitoring + +# Output to monitoring InfluxDB instance +[[outputs.influxdb_v3]] + urls = ["http://monitoring-influxdb:8181"] + database = "monitoring" + token = "MONITORING_AUTH_TOKEN" + +# Scrape metrics from InfluxDB 3 Core +[[inputs.prometheus]] + urls = ["http://localhost:8181/metrics"] + metric_version = 2 ``` -#### Using Prometheus relabeling +Replace {{% code-placeholder-key %}}`MONITORING_AUTH_TOKEN`{{% /code-placeholder-key %}} with your {{% token-link %}} for the monitoring InfluxDB instance. -If you use Prometheus to monitor your cluster, use relabeling to add node identification: +#### Start Telegraf -```yaml -relabel_configs: - - source_labels: [__address__] - target_label: node_name - regex: '([^:]+):.*' - replacement: '${1}' - - source_labels: [node_name] - target_label: node_role - regex: 'ingester-.*' - replacement: 'ingest' - - source_labels: [node_name] - target_label: node_role - regex: 'query-.*' - replacement: 'query' - - source_labels: [node_name] - target_label: node_role - regex: 'compactor-.*' - replacement: 'compact' +```bash +# Start Telegraf with the configuration +telegraf --config telegraf.conf + +# Run as a service (systemd example) +sudo systemctl start telegraf +sudo systemctl enable telegraf ``` -{{% /show-in %}} -{{% show-in "core" %}} -## Integration with monitoring tools +#### Query collected metrics + +Query the monitoring database using SQL: + +```sql +-- Request rate over time +SELECT + date_bin(INTERVAL '5 minutes', time) as time_bucket, + COUNT(*) as request_count +FROM http_requests_total +WHERE time >= now() - INTERVAL '1 hour' +GROUP BY time_bucket +ORDER BY time_bucket DESC; + +-- Error rate +SELECT + status, + COUNT(*) as error_count +FROM http_requests_total +WHERE time >= now() - INTERVAL '1 hour' + AND status IN ('client_error', 'server_error') +GROUP BY status; +``` -### Prometheus configuration +### Alternative: Prometheus configuration -Add {{< product-name >}} to your Prometheus configuration: +If you prefer Prometheus, add {{< product-name >}} to your Prometheus configuration: ```yaml # prometheus.yml From 7d8f3e6bf1768419fe899002004c786504c6f6f6 Mon Sep 17 00:00:00 2001 From: Jason Stirnaman Date: Thu, 2 Oct 2025 15:12:23 -0500 Subject: [PATCH 8/9] docs(influxdb3): Provide Telegraf config for Core and Enterprise cluster --- .../shared/influxdb3-admin/monitor-metrics.md | 130 +- dist/influxdb-version-detector.d.ts | 187 ++ dist/influxdb-version-detector.d.ts.map | 1 + dist/influxdb-version-detector.js | 2096 +++++++++++++++++ dist/influxdb-version-detector.js.map | 1 + dist/services/influxdb-urls.d.ts | 2 + dist/services/influxdb-urls.d.ts.map | 1 + dist/services/influxdb-urls.js | 3 + dist/services/influxdb-urls.js.map | 1 + dist/services/local-storage.d.ts | 30 + dist/services/local-storage.d.ts.map | 1 + dist/services/local-storage.js | 187 ++ dist/services/local-storage.js.map | 1 + 13 files changed, 2575 insertions(+), 66 deletions(-) create mode 100644 dist/influxdb-version-detector.d.ts create mode 100644 dist/influxdb-version-detector.d.ts.map create mode 100644 dist/influxdb-version-detector.js create mode 100644 dist/influxdb-version-detector.js.map create mode 100644 dist/services/influxdb-urls.d.ts create mode 100644 dist/services/influxdb-urls.d.ts.map create mode 100644 dist/services/influxdb-urls.js create mode 100644 dist/services/influxdb-urls.js.map create mode 100644 dist/services/local-storage.d.ts create mode 100644 dist/services/local-storage.d.ts.map create mode 100644 dist/services/local-storage.js create mode 100644 dist/services/local-storage.js.map diff --git a/content/shared/influxdb3-admin/monitor-metrics.md b/content/shared/influxdb3-admin/monitor-metrics.md index 1fa78ff496..8605571fc4 100644 --- a/content/shared/influxdb3-admin/monitor-metrics.md +++ b/content/shared/influxdb3-admin/monitor-metrics.md @@ -426,7 +426,7 @@ rate(object_store_transfer_objects_total[5m]) {{% show-in "enterprise" %}} ## Distributed monitoring setup -### Collect metrics with Telegraf (recommended) +### Collect metrics with Telegraf Use Telegraf to collect metrics from all cluster nodes and store them in a separate {{< product-name >}} instance for centralized monitoring. @@ -434,27 +434,53 @@ Use Telegraf to collect metrics from all cluster nodes and store them in a separ Create a Telegraf configuration file (`telegraf.conf`) to scrape metrics from your cluster nodes: -```toml +```toml { placeholders="MONITORING_AUTH_TOKEN|INGESTER_AUTH_TOKEN|QUERY_AUTH_TOKEN|COMPACTOR_AUTH_TOKEN" } # Telegraf configuration for InfluxDB 3 Enterprise monitoring # Output to monitoring InfluxDB instance -[[outputs.influxdb_v3]] +[[outputs.influxdb_v2]] urls = ["http://monitoring-influxdb:8181"] - database = "monitoring" token = "MONITORING_AUTH_TOKEN" + organization = "" + bucket = "monitoring" -# Scrape metrics from cluster nodes +# Scrape metrics from ingest nodes [[inputs.prometheus]] urls = [ "http://ingester-01:8181/metrics", - "http://ingester-02:8181/metrics", + "http://ingester-02:8181/metrics" + ] + metric_version = 2 + + # Authentication for metrics endpoint + [inputs.prometheus.headers] + Authorization = "Token INGESTER_AUTH_TOKEN" + + [inputs.prometheus.tags] + cluster = "production" + +# Scrape metrics from query nodes +[[inputs.prometheus]] + urls = [ "http://query-01:8181/metrics", - "http://query-02:8181/metrics", - "http://compactor-01:8181/metrics" + "http://query-02:8181/metrics" ] metric_version = 2 - # Add node identification from URL + [inputs.prometheus.headers] + Authorization = "Token QUERY_AUTH_TOKEN" + + [inputs.prometheus.tags] + cluster = "production" + +# Scrape metrics from compactor nodes +[[inputs.prometheus]] + urls = ["http://compactor-01:8181/metrics"] + metric_version = 2 + + [inputs.prometheus.headers] + Authorization = "Token COMPACTOR_AUTH_TOKEN" + [inputs.prometheus.tags] cluster = "production" @@ -478,6 +504,9 @@ Create a Telegraf configuration file (`telegraf.conf`) to scrape metrics from yo Replace the following: - {{% code-placeholder-key %}}`MONITORING_AUTH_TOKEN`{{% /code-placeholder-key %}}: your {{% token-link %}} for the monitoring InfluxDB instance +- {{% code-placeholder-key %}}`INGESTER_AUTH_TOKEN`{{% /code-placeholder-key %}}: your {{% token-link %}} with `system:metrics:read` permission for the ingest nodes +- {{% code-placeholder-key %}}`QUERY_AUTH_TOKEN`{{% /code-placeholder-key %}}: your {{% token-link %}} with `system:metrics:read` permission for the query nodes +- {{% code-placeholder-key %}}`COMPACTOR_AUTH_TOKEN`{{% /code-placeholder-key %}}: your {{% token-link %}} with `system:metrics:read` permission for the compactor node #### Start Telegraf @@ -514,31 +543,7 @@ WHERE time >= now() - INTERVAL '1 hour' GROUP BY node_name; ``` -### Alternative: Prometheus configuration - -If you prefer Prometheus, configure it to scrape all cluster nodes: - -```yaml -# prometheus.yml -scrape_configs: - - job_name: 'influxdb3-enterprise' - static_configs: - - targets: - - 'ingester-01:8181' - - 'ingester-02:8181' - - 'query-01:8181' - - 'query-02:8181' - - 'compactor-01:8181' - - 'processor-01:8181' - metrics_path: '/metrics' - scrape_interval: 30s - relabel_configs: - - source_labels: [__address__] - target_label: node_name - regex: '([^:]+):.*' - replacement: '${1}' -``` - + + + ### Add node identification with Prometheus -If using Prometheus instead of Telegraf, add node identification through relabeling: +If using Prometheus instead of Telegraf, add node identification through _relabeling_: ```yaml # prometheus.yml @@ -648,22 +658,12 @@ scrape_configs: regex: 'compactor-.*' replacement: 'compact' ``` - -Query metrics with node labels in PromQL: - -```promql -# Request rate by node and role -sum(rate(http_requests_total[5m])) by (node_name, node_role) - -# Filter metrics for specific node role -http_requests_total{node_role="ingest"} -``` {{% /show-in %}} {{% show-in "core" %}} ## Integration with monitoring tools -### Collect metrics with Telegraf (recommended) +### Collect metrics with Telegraf Use Telegraf to collect metrics and store them in a separate {{< product-name >}} instance for monitoring. @@ -671,22 +671,30 @@ Use Telegraf to collect metrics and store them in a separate {{< product-name >} Create a Telegraf configuration file (`telegraf.conf`): -```toml +```toml { placeholders="MONITORING_AUTH_TOKEN|AUTH_TOKEN" } # Telegraf configuration for InfluxDB 3 Core monitoring # Output to monitoring InfluxDB instance -[[outputs.influxdb_v3]] +[[outputs.influxdb_v2]] urls = ["http://monitoring-influxdb:8181"] - database = "monitoring" token = "MONITORING_AUTH_TOKEN" + organization = "" + bucket = "monitoring" # Scrape metrics from InfluxDB 3 Core [[inputs.prometheus]] urls = ["http://localhost:8181/metrics"] metric_version = 2 + + # Authentication for metrics endpoint (if required) + # [inputs.prometheus.headers] + # Authorization = "Token AUTH_TOKEN" ``` -Replace {{% code-placeholder-key %}}`MONITORING_AUTH_TOKEN`{{% /code-placeholder-key %}} with your {{% token-link %}} for the monitoring InfluxDB instance. +Replace the following: + +- {{% code-placeholder-key %}}`MONITORING_AUTH_TOKEN`{{% /code-placeholder-key %}}: your {{% token-link %}} for the monitoring InfluxDB instance +- {{% code-placeholder-key %}}`AUTH_TOKEN`{{% /code-placeholder-key %}} (if uncommented): your {{% token-link %}} for accessing the `/metrics` endpoint #### Start Telegraf @@ -723,20 +731,7 @@ WHERE time >= now() - INTERVAL '1 hour' GROUP BY status; ``` -### Alternative: Prometheus configuration - -If you prefer Prometheus, add {{< product-name >}} to your Prometheus configuration: - -```yaml -# prometheus.yml -scrape_configs: - - job_name: 'influxdb3-core' - static_configs: - - targets: ['localhost:8181'] - metrics_path: '/metrics' - scrape_interval: 30s -``` - + + {{% /show-in %}} ## Best practices @@ -803,4 +801,4 @@ groups: 6. **Monitor shared resources**: Object store performance affects all nodes 7. **Track catalog conflicts**: High retry rates indicate coordination issues 8. **Regularly review dashboards and alerts**: Adjust as cluster usage patterns evolve -{{% /show-in %}} \ No newline at end of file +{{% /show-in %}} diff --git a/dist/influxdb-version-detector.d.ts b/dist/influxdb-version-detector.d.ts new file mode 100644 index 0000000000..45870b3939 --- /dev/null +++ b/dist/influxdb-version-detector.d.ts @@ -0,0 +1,187 @@ +/** + * InfluxDB Version Detector Component + * + * Helps users identify which InfluxDB product they're using through a + * guided questionnaire with URL detection and scoring-based recommendations. + * + * DECISION TREE LOGIC (from .context/drafts/influxdb-version-detector/influxdb-decision-tree.md): + * + * ## Primary Detection Flow + * + * START: User enters URL + * | + * ā”œā”€ā†’ URL matches known cloud patterns? + * │ │ + * │ ā”œā”€ā†’ YES: Contains "influxdb.io" → **InfluxDB Cloud Dedicated** āœ“ + * │ ā”œā”€ā†’ YES: Contains "cloud2.influxdata.com" regions → **InfluxDB Cloud Serverless** āœ“ + * │ ā”œā”€ā†’ YES: Contains "influxcloud.net" → **InfluxDB Cloud 1** āœ“ + * │ └─→ YES: Contains other cloud2 regions → **InfluxDB Cloud (TSM)** āœ“ + * │ + * └─→ NO: Check port and try /ping endpoint + * │ + * ā”œā”€ā†’ Port 8181 detected? → Strong indicator of v3 (Core/Enterprise) + * | | Returns 200 (auth successful or disabled)? + * | │ │--> `x-influxdb-build: Enterprise` -> **InfluxDB 3 Enterprise** āœ“ (definitive) + * | │ │--> `x-influxdb-build: Core` -> **InfluxDB 3 Core** āœ“ (definitive) + * │ │ + * │ ā”œā”€ā†’ Returns 401 Unauthorized (default - auth required)? + * │ │ + * │ └─→ Ask "Paid or Free?" + * │ ā”œā”€ā†’ Paid → **InfluxDB 3 Enterprise** āœ“ (definitive) + * │ └─→ Free → **InfluxDB 3 Core** āœ“ (definitive) + * | + * ā”œā”€ā†’ Port 8086 detected? → Strong indicator of legacy (OSS/Enterprise) + * │ │ āš ļø NOTE: v1.x ping auth optional (ping-auth-enabled), v2.x always open + * │ │ + * │ ā”œā”€ā†’ Returns 401 Unauthorized? + * │ │ │ Could be v1.x with ping-auth-enabled=true OR Enterprise + * │ │ │ + * │ │ └─→ Ask "Paid or Free?" → Show ranked results + * │ │ + * │ ā”œā”€ā†’ Returns 200/204 (accessible)? + * │ │ │ Likely v2.x OSS (always open) or v1.x with ping-auth-enabled=false + * │ │ │ + * │ │ └─→ Continue to questionnaire + * │ + * └─→ Blocked/Can't detect? + * │ + * └─→ Start questionnaire + * + * ## Questionnaire Flow (No URL or after detection) + * + * Q1: Which type of license do you have? + * ā”œā”€ā†’ Paid/Commercial License + * ā”œā”€ā†’ Free/Open Source (including free cloud tiers) + * └─→ I'm not sure + * + * Q2: Is your InfluxDB hosted by InfluxData (cloud) or self-hosted? + * ā”œā”€ā†’ Cloud service (hosted by InfluxData) + * ā”œā”€ā†’ Self-hosted (on your own servers) + * └─→ I'm not sure + * + * Q3: How long has your server been in place? + * ā”œā”€ā†’ Recently installed (less than 1 year) + * ā”œā”€ā†’ 1-5 years + * ā”œā”€ā†’ More than 5 years + * └─→ I'm not sure + * + * Q4: Which query language(s) do you use? + * ā”œā”€ā†’ SQL + * ā”œā”€ā†’ InfluxQL + * ā”œā”€ā†’ Flux + * ā”œā”€ā†’ Multiple languages + * └─→ I'm not sure + * + * ## Definitive Determinations (Stop immediately, no more questions) + * + * 1. **401 + Port 8181 + Paid** → InfluxDB 3 Enterprise āœ“ + * 2. **401 + Port 8181 + Free** → InfluxDB 3 Core āœ“ + * 3. **URL matches cloud pattern** → Specific cloud product āœ“ + * 4. **x-influxdb-build header** → Definitive product identification āœ“ + * + * ## Scoring System (When not definitive) + * + * ### Elimination Rules + * - **Free + Self-hosted** → Eliminates all cloud products + * - **Free** → Eliminates: 3 Enterprise, Enterprise, Clustered, Cloud Dedicated, Cloud 1 + * - **Paid + Self-hosted** → Eliminates all cloud products + * - **Paid + Cloud** → Eliminates all self-hosted products + * - **Free + Cloud** → Eliminates all self-hosted products, favors Serverless/TSM + * + * ### Strong Signals (High points) + * - **401 Response**: +50 for v3 products, +30 for Clustered + * - **Port 8181**: +30 for v3 products + * - **Port 8086**: +20 for legacy products + * - **SQL Language**: +40 for v3 products, eliminates v1/v2 + * - **Flux Language**: +30 for v2 era, eliminates v1 and v3 + * - **Server Age 5+ years**: +30 for v1 products, -50 for v3 + * + * ### Ranking Display Rules + * - Only show "Most Likely" if: + * - Top score > 30 (not low confidence) + * - AND difference between #1 and #2 is ≄ 15 points + * - Show manual verification commands only if: + * - Confidence is not high (score < 60) + * - AND it's a self-hosted product + * - AND user didn't say it's cloud + */ +interface ComponentOptions { + component: HTMLElement; +} +declare global { + interface Window { + gtag?: (_event: string, _action: string, _parameters?: Record) => void; + } +} +declare class InfluxDBVersionDetector { + private container; + private products; + private influxdbUrls; + private answers; + private initialized; + private questionFlow; + private currentQuestionIndex; + private questionHistory; + private progressBar; + private resultDiv; + private restartBtn; + private currentContext; + constructor(options: ComponentOptions); + private parseComponentData; + private init; + private setupPlaceholders; + private setupPingHeadersPlaceholder; + private setupDockerOutputPlaceholder; + private getCurrentPageSection; + private trackAnalyticsEvent; + private initializeForModal; + private getBasicUrlSuggestion; + private getProductDisplayName; + private generateConfigurationGuidance; + private getHostExample; + private usesDatabaseTerminology; + private getAuthenticationInfo; + private detectEnterpriseFeatures; + private analyzeUrlPatterns; + private render; + private attachEventListeners; + private updateProgress; + private showQuestion; + private enhanceUrlInputWithSuggestions; + private getCurrentProduct; + private handleUrlKnown; + private goBack; + private detectByUrl; + private detectContext; + private detectPortFromUrl; + private startQuestionnaire; + private startQuestionnaireWithCloudContext; + private answerQuestion; + private handleAuthorizationHelp; + private showRankedResults; + /** + * Gets the Grafana documentation link for a given product + */ + private getGrafanaLink; + /** + * Generates a unified product result block with characteristics and Grafana link + */ + private generateProductResult; + /** + * Maps simple product keys (used in URL detection) to full product names (used in scoring) + */ + private mapProductKeyToFullName; + private applyScoring; + private displayRankedResults; + private analyzePingHeaders; + private showResult; + private analyzeDockerOutput; + private showPingTestSuggestion; + private showOSSVersionCheckSuggestion; + private showMultipleCandidatesSuggestion; + private showDetectedVersion; + private restart; +} +export default function initInfluxDBVersionDetector(options: ComponentOptions): InfluxDBVersionDetector; +export {}; +//# sourceMappingURL=influxdb-version-detector.d.ts.map \ No newline at end of file diff --git a/dist/influxdb-version-detector.d.ts.map b/dist/influxdb-version-detector.d.ts.map new file mode 100644 index 0000000000..c120e96c7e --- /dev/null +++ b/dist/influxdb-version-detector.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"influxdb-version-detector.d.ts","sourceRoot":"","sources":["../assets/js/influxdb-version-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0GG;AAuCH,UAAU,gBAAgB;IACxB,SAAS,EAAE,WAAW,CAAC;CACxB;AAaD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,IAAI,CAAC,EAAE,CACL,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAClC,IAAI,CAAC;KACX;CACF;AAED,cAAM,uBAAuB;IAC3B,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,cAAc,CAA+C;gBAEzD,OAAO,EAAE,gBAAgB;IAoBrC,OAAO,CAAC,kBAAkB;IAqC1B,OAAO,CAAC,IAAI;IAcZ,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,2BAA2B;IAoCnC,OAAO,CAAC,4BAA4B;IA+BpC,OAAO,CAAC,qBAAqB;IAe7B,OAAO,CAAC,mBAAmB;IAsG3B,OAAO,CAAC,kBAAkB;IAqC1B,OAAO,CAAC,qBAAqB;IAK7B,OAAO,CAAC,qBAAqB;IAgC7B,OAAO,CAAC,6BAA6B;IA4FrC,OAAO,CAAC,cAAc;IAgCtB,OAAO,CAAC,uBAAuB;IAU/B,OAAO,CAAC,qBAAqB;IA2B7B,OAAO,CAAC,wBAAwB;IAehC,OAAO,CAAC,kBAAkB;IAmJ1B,OAAO,CAAC,MAAM;IAsNd,OAAO,CAAC,oBAAoB;IA8G5B,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,YAAY;IAsBpB,OAAO,CAAC,8BAA8B;IAyCtC,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,cAAc;IAuBtB,OAAO,CAAC,MAAM;YA2BA,WAAW;IA6DzB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,iBAAiB;IAgBzB,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,kCAAkC;IAU1C,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,uBAAuB;IAqG/B,OAAO,CAAC,iBAAiB;IAoCzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAmBtB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgE7B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAiB/B,OAAO,CAAC,YAAY;IAmKpB,OAAO,CAAC,oBAAoB;IAiJ5B,OAAO,CAAC,kBAAkB;IAmG1B,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,mBAAmB;IA2F3B,OAAO,CAAC,sBAAsB;IA4D9B,OAAO,CAAC,6BAA6B;IAiDrC,OAAO,CAAC,gCAAgC;IA2CxC,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,OAAO;CA2ChB;AAGD,MAAM,CAAC,OAAO,UAAU,2BAA2B,CACjD,OAAO,EAAE,gBAAgB,GACxB,uBAAuB,CAEzB"} \ No newline at end of file diff --git a/dist/influxdb-version-detector.js b/dist/influxdb-version-detector.js new file mode 100644 index 0000000000..2941fd0489 --- /dev/null +++ b/dist/influxdb-version-detector.js @@ -0,0 +1,2096 @@ +/** + * InfluxDB Version Detector Component + * + * Helps users identify which InfluxDB product they're using through a + * guided questionnaire with URL detection and scoring-based recommendations. + * + * DECISION TREE LOGIC (from .context/drafts/influxdb-version-detector/influxdb-decision-tree.md): + * + * ## Primary Detection Flow + * + * START: User enters URL + * | + * ā”œā”€ā†’ URL matches known cloud patterns? + * │ │ + * │ ā”œā”€ā†’ YES: Contains "influxdb.io" → **InfluxDB Cloud Dedicated** āœ“ + * │ ā”œā”€ā†’ YES: Contains "cloud2.influxdata.com" regions → **InfluxDB Cloud Serverless** āœ“ + * │ ā”œā”€ā†’ YES: Contains "influxcloud.net" → **InfluxDB Cloud 1** āœ“ + * │ └─→ YES: Contains other cloud2 regions → **InfluxDB Cloud (TSM)** āœ“ + * │ + * └─→ NO: Check port and try /ping endpoint + * │ + * ā”œā”€ā†’ Port 8181 detected? → Strong indicator of v3 (Core/Enterprise) + * | | Returns 200 (auth successful or disabled)? + * | │ │--> `x-influxdb-build: Enterprise` -> **InfluxDB 3 Enterprise** āœ“ (definitive) + * | │ │--> `x-influxdb-build: Core` -> **InfluxDB 3 Core** āœ“ (definitive) + * │ │ + * │ ā”œā”€ā†’ Returns 401 Unauthorized (default - auth required)? + * │ │ + * │ └─→ Ask "Paid or Free?" + * │ ā”œā”€ā†’ Paid → **InfluxDB 3 Enterprise** āœ“ (definitive) + * │ └─→ Free → **InfluxDB 3 Core** āœ“ (definitive) + * | + * ā”œā”€ā†’ Port 8086 detected? → Strong indicator of legacy (OSS/Enterprise) + * │ │ āš ļø NOTE: v1.x ping auth optional (ping-auth-enabled), v2.x always open + * │ │ + * │ ā”œā”€ā†’ Returns 401 Unauthorized? + * │ │ │ Could be v1.x with ping-auth-enabled=true OR Enterprise + * │ │ │ + * │ │ └─→ Ask "Paid or Free?" → Show ranked results + * │ │ + * │ ā”œā”€ā†’ Returns 200/204 (accessible)? + * │ │ │ Likely v2.x OSS (always open) or v1.x with ping-auth-enabled=false + * │ │ │ + * │ │ └─→ Continue to questionnaire + * │ + * └─→ Blocked/Can't detect? + * │ + * └─→ Start questionnaire + * + * ## Questionnaire Flow (No URL or after detection) + * + * Q1: Which type of license do you have? + * ā”œā”€ā†’ Paid/Commercial License + * ā”œā”€ā†’ Free/Open Source (including free cloud tiers) + * └─→ I'm not sure + * + * Q2: Is your InfluxDB hosted by InfluxData (cloud) or self-hosted? + * ā”œā”€ā†’ Cloud service (hosted by InfluxData) + * ā”œā”€ā†’ Self-hosted (on your own servers) + * └─→ I'm not sure + * + * Q3: How long has your server been in place? + * ā”œā”€ā†’ Recently installed (less than 1 year) + * ā”œā”€ā†’ 1-5 years + * ā”œā”€ā†’ More than 5 years + * └─→ I'm not sure + * + * Q4: Which query language(s) do you use? + * ā”œā”€ā†’ SQL + * ā”œā”€ā†’ InfluxQL + * ā”œā”€ā†’ Flux + * ā”œā”€ā†’ Multiple languages + * └─→ I'm not sure + * + * ## Definitive Determinations (Stop immediately, no more questions) + * + * 1. **401 + Port 8181 + Paid** → InfluxDB 3 Enterprise āœ“ + * 2. **401 + Port 8181 + Free** → InfluxDB 3 Core āœ“ + * 3. **URL matches cloud pattern** → Specific cloud product āœ“ + * 4. **x-influxdb-build header** → Definitive product identification āœ“ + * + * ## Scoring System (When not definitive) + * + * ### Elimination Rules + * - **Free + Self-hosted** → Eliminates all cloud products + * - **Free** → Eliminates: 3 Enterprise, Enterprise, Clustered, Cloud Dedicated, Cloud 1 + * - **Paid + Self-hosted** → Eliminates all cloud products + * - **Paid + Cloud** → Eliminates all self-hosted products + * - **Free + Cloud** → Eliminates all self-hosted products, favors Serverless/TSM + * + * ### Strong Signals (High points) + * - **401 Response**: +50 for v3 products, +30 for Clustered + * - **Port 8181**: +30 for v3 products + * - **Port 8086**: +20 for legacy products + * - **SQL Language**: +40 for v3 products, eliminates v1/v2 + * - **Flux Language**: +30 for v2 era, eliminates v1 and v3 + * - **Server Age 5+ years**: +30 for v1 products, -50 for v3 + * + * ### Ranking Display Rules + * - Only show "Most Likely" if: + * - Top score > 30 (not low confidence) + * - AND difference between #1 and #2 is ≄ 15 points + * - Show manual verification commands only if: + * - Confidence is not high (score < 60) + * - AND it's a self-hosted product + * - AND user didn't say it's cloud + */ +import { getInfluxDBUrls } from './services/local-storage.js'; +class InfluxDBVersionDetector { + constructor(options) { + this.answers = {}; + this.initialized = false; + this.questionFlow = []; + this.currentQuestionIndex = 0; + this.questionHistory = []; // Track question history for back navigation + this.progressBar = null; + this.resultDiv = null; + this.restartBtn = null; + this.currentContext = 'questionnaire'; + this.container = options.component; + // Parse data attributes from the component element + const { products, influxdbUrls } = this.parseComponentData(); + this.products = products; + this.influxdbUrls = influxdbUrls; + // Check if component is in a modal + const modal = this.container.closest('.modal-content'); + if (modal) { + // If in modal, wait for modal to be opened before initializing + this.initializeForModal(); + } + else { + // If not in modal, initialize immediately + this.init(); + } + } + parseComponentData() { + let products = {}; + let influxdbUrls = {}; + // Parse products data - Hugo always provides this data + const productsData = this.container.getAttribute('data-products'); + if (productsData) { + try { + products = JSON.parse(productsData); + } + catch (error) { + console.warn('Failed to parse products data:', error); + } + } + // Parse influxdb URLs data + const influxdbUrlsData = this.container.getAttribute('data-influxdb-urls'); + if (influxdbUrlsData && influxdbUrlsData !== '#ZgotmplZ') { + try { + influxdbUrls = JSON.parse(influxdbUrlsData); + } + catch (error) { + console.warn('Failed to parse influxdb_urls data:', error); + influxdbUrls = {}; // Fallback to empty object + } + } + else { + console.debug('InfluxDB URLs data not available or blocked by template security. ' + + 'This is expected when Hugo data is unavailable.'); + influxdbUrls = {}; // Fallback to empty object + } + return { products, influxdbUrls }; + } + init() { + this.render(); + this.setupPlaceholders(); + this.attachEventListeners(); + this.showQuestion('q-url-known'); + this.initialized = true; + // Track modal opening + this.trackAnalyticsEvent({ + interaction_type: 'modal_opened', + section: this.getCurrentPageSection(), + }); + } + setupPlaceholders() { + // This method is called at init but some placeholders need to be set + // when questions are actually displayed since DOM elements don't exist yet + } + setupPingHeadersPlaceholder() { + const pingHeaders = this.container.querySelector('#ping-headers'); + if (pingHeaders) { + const exampleContent = [ + '# Replace this with your actual response headers', + '# Example formats:', + '', + '# InfluxDB 3 Core:', + 'HTTP/1.1 200 OK', + 'x-influxdb-build: core', + 'x-influxdb-version: 3.1.0', + '', + '# InfluxDB 3 Enterprise:', + 'HTTP/1.1 200 OK', + 'x-influxdb-build: enterprise', + 'x-influxdb-version: 3.1.0', + '', + '# InfluxDB v2 OSS:', + 'HTTP/1.1 204 No Content', + 'X-Influxdb-Build: OSS', + 'X-Influxdb-Version: 2.7.8', + '', + '# InfluxDB v1:', + 'HTTP/1.1 204 No Content', + 'X-Influxdb-Version: 1.8.10', + ].join('\n'); + pingHeaders.value = exampleContent; + // Select all text when user clicks in the textarea so they can easily replace it + pingHeaders.addEventListener('focus', () => { + pingHeaders.select(); + }); + } + } + setupDockerOutputPlaceholder() { + const dockerOutput = this.container.querySelector('#docker-output'); + if (dockerOutput) { + const exampleContent = [ + '# Replace this with your actual command output', + '# Example formats:', + '', + '# Version command output:', + 'InfluxDB 3.1.0 (git: abc123def)', + 'or', + 'InfluxDB v2.7.8 (git: 407fa622e)', + '', + '# Ping headers from curl -I:', + 'HTTP/1.1 200 OK', + 'x-influxdb-build: core', + 'x-influxdb-version: 3.1.0', + '', + '# Startup logs:', + '2024-01-01T00:00:00.000Z info InfluxDB starting', + '2024-01-01T00:00:00.000Z info InfluxDB 3.1.0 (git: abc123)', + ].join('\n'); + dockerOutput.value = exampleContent; + // Select all text when user clicks in the textarea so they can easily replace it + dockerOutput.addEventListener('focus', () => { + dockerOutput.select(); + }); + } + } + getCurrentPageSection() { + // Extract meaningful section from current page + const path = window.location.pathname; + const pathSegments = path.split('/').filter((segment) => segment); + // Try to get a meaningful section name + if (pathSegments.length >= 3) { + return pathSegments.slice(0, 3).join('/'); // e.g., "influxdb3/core/visualize-data" + } + else if (pathSegments.length >= 2) { + return pathSegments.slice(0, 2).join('/'); // e.g., "influxdb3/core" + } + return path || 'unknown'; + } + trackAnalyticsEvent(eventData) { + // Track Google Analytics events following the pattern from code-controls.js + try { + // Get current page context + const currentUrl = new URL(window.location.href); + const path = window.location.pathname; + // Determine product context from current page + let pageContext = 'other'; + if (/\/influxdb\/cloud\//.test(path)) { + pageContext = 'cloud'; + } + else if (/\/influxdb3\/core/.test(path)) { + pageContext = 'core'; + } + else if (/\/influxdb3\/enterprise/.test(path)) { + pageContext = 'enterprise'; + } + else if (/\/influxdb3\/cloud-serverless/.test(path)) { + pageContext = 'serverless'; + } + else if (/\/influxdb3\/cloud-dedicated/.test(path)) { + pageContext = 'dedicated'; + } + else if (/\/influxdb3\/clustered/.test(path)) { + pageContext = 'clustered'; + } + else if (/\/(enterprise_|influxdb).*\/v[1-2]\//.test(path)) { + pageContext = 'oss/enterprise'; + } + // Add tracking parameters to URL (following code-controls.js pattern) + if (eventData.detected_product) { + switch (eventData.detected_product) { + case 'core': + currentUrl.searchParams.set('dl', 'oss3'); + break; + case 'enterprise': + currentUrl.searchParams.set('dl', 'enterprise'); + break; + case 'cloud': + case 'cloud-v1': + case 'cloud-v2-tsm': + currentUrl.searchParams.set('dl', 'cloud'); + break; + case 'serverless': + currentUrl.searchParams.set('dl', 'serverless'); + break; + case 'dedicated': + currentUrl.searchParams.set('dl', 'dedicated'); + break; + case 'clustered': + currentUrl.searchParams.set('dl', 'clustered'); + break; + case 'oss': + case 'oss-v1': + case 'oss-v2': + currentUrl.searchParams.set('dl', 'oss'); + break; + } + } + // Add additional tracking parameters + if (eventData.detection_method) { + currentUrl.searchParams.set('detection_method', eventData.detection_method); + } + if (eventData.completion_status) { + currentUrl.searchParams.set('completion', eventData.completion_status); + } + if (eventData.section) { + currentUrl.searchParams.set('section', encodeURIComponent(eventData.section)); + } + // Update browser history without triggering page reload + if (window.history && window.history.replaceState) { + window.history.replaceState(null, '', currentUrl.toString()); + } + // Send custom Google Analytics event if gtag is available + if (typeof window.gtag !== 'undefined') { + window.gtag('event', 'influxdb_version_detector', { + interaction_type: eventData.interaction_type, + detected_product: eventData.detected_product, + detection_method: eventData.detection_method, + completion_status: eventData.completion_status, + question_id: eventData.question_id, + answer_value: eventData.answer_value, + section: eventData.section, + page_context: pageContext, + custom_map: { + dimension1: eventData.detected_product, + dimension2: eventData.detection_method, + dimension3: pageContext, + }, + }); + } + } + catch (error) { + // Silently handle analytics errors to avoid breaking functionality + console.debug('Analytics tracking error:', error); + } + } + initializeForModal() { + // Set up event listener to initialize when modal opens + const modalContent = this.container.closest('.modal-content'); + if (!modalContent) + return; + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.type === 'attributes' && + mutation.attributeName === 'style') { + const target = mutation.target; + const isVisible = target.style.display !== 'none' && target.style.display !== ''; + if (isVisible && !this.initialized) { + // Modal just opened and component not yet initialized + this.init(); + observer.disconnect(); + } + } + }); + }); + // Start observing the modal content for style changes + observer.observe(modalContent, { + attributes: true, + attributeFilter: ['style'], + }); + // Also check if modal is already visible + const computedStyle = window.getComputedStyle(modalContent); + if (computedStyle.display !== 'none' && !this.initialized) { + this.init(); + observer.disconnect(); + } + } + getBasicUrlSuggestion() { + // Provide a basic placeholder URL suggestion based on common patterns + return 'https://your-influxdb-host.com:8086'; + } + getProductDisplayName(product) { + const displayNames = { + // Simplified product keys (used in detection results) + 'oss-v1': 'InfluxDB OSS v1.x', + 'oss-v2': 'InfluxDB OSS v2.x', + oss: 'InfluxDB OSS (version unknown)', + cloud: 'InfluxDB Cloud', + 'cloud-v1': 'InfluxDB Cloud v1', + 'cloud-v2-tsm': 'InfluxDB Cloud v2 (TSM)', + serverless: 'InfluxDB Cloud Serverless', + core: 'InfluxDB 3 Core', + enterprise: 'InfluxDB 3 Enterprise', + dedicated: 'InfluxDB Cloud Dedicated', + clustered: 'InfluxDB Clustered', + custom: 'Custom URL', + // Raw product keys from products.yml (used in scoring) + influxdb3_core: 'InfluxDB 3 Core', + influxdb3_enterprise: 'InfluxDB 3 Enterprise', + influxdb3_cloud_serverless: 'InfluxDB Cloud Serverless', + influxdb3_cloud_dedicated: 'InfluxDB Cloud Dedicated', + influxdb3_clustered: 'InfluxDB Clustered', + influxdb_v1: 'InfluxDB OSS v1.x', + influxdb_v2: 'InfluxDB OSS v2.x', + enterprise_influxdb: 'InfluxDB Enterprise v1.x', + influxdb: 'InfluxDB OSS v2.x', + }; + displayNames['core or enterprise'] = + `${displayNames.core} or ${displayNames.enterprise}`; + return displayNames[product] || product; + } + generateConfigurationGuidance(productKey) { + // Map from result product names to products.yml keys + const productMapping = { + core: 'influxdb3_core', + enterprise: 'influxdb3_enterprise', + serverless: 'influxdb3_cloud_serverless', + dedicated: 'influxdb3_cloud_dedicated', + clustered: 'influxdb3_clustered', + 'oss-v1': 'influxdb_v1', + 'oss-v2': 'influxdb_v2', + }; + const dataKey = productMapping[productKey]; + if (!dataKey || !this.products[dataKey]) { + return ''; + } + const productConfig = this.products[dataKey]; + const productName = this.getProductDisplayName(productKey); + if (!productConfig.query_languages || + Object.keys(productConfig.query_languages).length === 0) { + return ''; + } + let html = ` +
+

Configuration Parameter Meanings for ${productName}

+

When configuring Grafana or other tools to connect to your ${productName} instance, these parameters mean:

+ `; + // Add HOST explanation + const hostExample = this.getHostExample(dataKey); + html += ` +
+ HOST/URL: The network address where your ${productName} instance is running
+ + For your setup, this would typically be: ${hostExample} + +
+ `; + // Add database/bucket terminology explanation + const usesDatabase = this.usesDatabaseTerminology(productConfig); + if (usesDatabase) { + html += ` +
+ DATABASE: The named collection where your data is stored
+ + ${productName} uses "database" terminology for organizing your time series data + +
+ `; + } + else { + html += ` +
+ BUCKET: The named collection where your data is stored
+ + ${productName} uses "bucket" terminology for organizing your time series data + +
+ `; + } + // Add authentication explanation + const authInfo = this.getAuthenticationInfo(productConfig); + html += ` +
+ AUTHENTICATION: ${authInfo.description}
+ + ${authInfo.details} + +
+ `; + // Add query language explanation + const languages = Object.keys(productConfig.query_languages).join(', '); + html += ` +
+ QUERY LANGUAGE: The syntax used to retrieve your data
+ + ${productName} supports: ${languages} + +
+ `; + html += '
'; + return html; + } + getHostExample(productDataKey) { + // Extract placeholder_host from the products data if available + const productData = this.products[productDataKey]; + // Use placeholder_host from the product configuration if available + if (productData?.placeholder_host) { + // Add protocol if not present + const host = productData.placeholder_host; + if (host.startsWith('http://') || host.startsWith('https://')) { + return host; + } + else { + // Default to http for localhost, https for others + return host.includes('localhost') + ? `http://${host}` + : `https://${host}`; + } + } + // Fallback based on product type + const hostExamples = { + influxdb3_core: 'http://localhost:8181', + influxdb3_enterprise: 'http://localhost:8181', + influxdb3_cloud_serverless: 'https://cloud2.influxdata.com', + influxdb3_cloud_dedicated: 'https://cluster-id.a.influxdb.io', + influxdb3_clustered: 'https://cluster-host.com', + influxdb_v1: 'http://localhost:8086', + influxdb_v2: 'http://localhost:8086', + }; + return hostExamples[productDataKey] || 'http://localhost:8086'; + } + usesDatabaseTerminology(productConfig) { + // Check if any query language uses 'Database' parameter + for (const language of Object.values(productConfig.query_languages)) { + if (language.required_params.includes('Database')) { + return true; + } + } + return false; + } + getAuthenticationInfo(productConfig) { + // Check if any query language requires Token + const requiresToken = Object.values(productConfig.query_languages).some((lang) => lang.required_params.includes('Token')); + // Determine if this product uses "database" or "bucket" terminology + const usesDatabaseTerm = this.usesDatabaseTerminology(productConfig); + const resourceName = usesDatabaseTerm ? 'database' : 'bucket'; + if (requiresToken) { + return { + description: 'Token-based authentication required', + details: `You need a valid API token with appropriate permissions for your ${resourceName}`, + }; + } + else { + return { + description: 'No authentication required by default', + details: 'This instance typically runs without authentication, though it may be optionally configured', + }; + } + } + detectEnterpriseFeatures() { + // According to the decision tree, we cannot reliably distinguish + // Core vs Enterprise from URL alone. The real differentiator is: + // - Both Enterprise and Core: /ping requires auth by default (opt-out possible) + // - Definitive identification requires x-influxdb-build header from 200 response + // + // Since this component cannot make HTTP requests to test /ping, + // we return null to indicate we cannot distinguish them from URL alone. + return null; + } + analyzeUrlPatterns(url) { + if (!url || !this.influxdbUrls) { + return { likelyProduct: null, confidence: 0 }; + } + const urlLower = url.toLowerCase(); + // PRIORITY 1: Check for definitive cloud patterns first (per decision tree) + // These should be checked before localhost patterns for accuracy + // InfluxDB Cloud Dedicated: Contains "influxdb.io" + if (urlLower.includes('influxdb.io')) { + return { likelyProduct: 'dedicated', confidence: 1.0 }; + } + // InfluxDB Cloud Serverless: Contains "cloud2.influxdata.com" regions + if (urlLower.includes('cloud2.influxdata.com')) { + // Check for specific Serverless regions + const serverlessRegions = [ + 'us-east-1-1.aws.cloud2.influxdata.com', + 'eu-central-1-1.aws.cloud2.influxdata.com', + ]; + for (const region of serverlessRegions) { + if (urlLower.includes(region.toLowerCase())) { + return { likelyProduct: 'serverless', confidence: 1.0 }; + } + } + // Other cloud2 regions default to InfluxDB Cloud v2 (TSM) + return { likelyProduct: 'cloud-v2-tsm', confidence: 0.9 }; + } + // InfluxDB Cloud v1 (legacy): Contains "influxcloud.net" + if (urlLower.includes('influxcloud.net')) { + return { likelyProduct: 'cloud-v1', confidence: 1.0 }; + } + // PRIORITY 2: Check for localhost/port-based patterns (OSS, Core, Enterprise) + // Note: localhost URLs cannot be cloud versions - they're always self-hosted + if (urlLower.includes('localhost') || urlLower.includes('127.0.0.1')) { + // OSS default port + if (urlLower.includes(':8086')) { + return { + likelyProduct: 'oss', + confidence: 0.8, + suggestion: 'version-check', + }; + } + // Core/Enterprise default port - both use 8181 + if (urlLower.includes(':8181')) { + // Try to distinguish between Core and Enterprise + const enterpriseResult = this.detectEnterpriseFeatures(); + if (enterpriseResult) { + return enterpriseResult; + } + // Can't distinguish from URL alone - suggest ping test + return { + likelyProduct: 'core or enterprise', + confidence: 0.7, + suggestion: 'ping-test', + }; + } + } + // Then check cloud products with provider regions + // Skip this check if URL is localhost (cannot be cloud) + const isLocalhost = urlLower.includes('localhost') || urlLower.includes('127.0.0.1'); + if (!isLocalhost) { + for (const [productKey, productData] of Object.entries(this.influxdbUrls)) { + if (!productData || typeof productData !== 'object') + continue; + const providers = productData.providers; + if (!Array.isArray(providers)) + continue; + for (const provider of providers) { + if (!provider.regions) + continue; + for (const region of provider.regions) { + if (region.url) { + const patternUrl = region.url.toLowerCase(); + // Exact match + if (urlLower === patternUrl) { + return { likelyProduct: productKey, confidence: 1.0 }; + } + // Domain match for cloud URLs + if (productKey === 'cloud' && + urlLower.includes('cloud2.influxdata.com')) { + return { likelyProduct: 'cloud', confidence: 0.9 }; + } + } + } + } + } + } + // Additional heuristics based on common patterns + // Special handling for user inputs like "cloud 2", "cloud v2", etc. + // Skip cloud heuristics for localhost URLs + if (!isLocalhost) { + if (urlLower.match(/cloud\s*[v]?2/)) { + return { likelyProduct: 'cloud', confidence: 0.8 }; + } + if (urlLower.includes('cloud') || + urlLower.includes('aws') || + urlLower.includes('azure') || + urlLower.includes('gcp')) { + return { likelyProduct: 'cloud', confidence: 0.6 }; + } + } + // Port-based suggestions for unknown/invalid URLs + if (urlLower.includes(':8086')) { + return { + likelyProduct: 'oss-port', + confidence: 0.4, + suggestion: 'multiple-candidates-8086', + }; + } + if (urlLower.includes(':8181')) { + return { + likelyProduct: 'v3-port', + confidence: 0.4, + suggestion: 'multiple-candidates-8181', + }; + } + return { likelyProduct: null, confidence: 0 }; + } + render() { + this.container.innerHTML = ` +
+

+ InfluxDB product detector +

+

+ Answer a few questions to identify which InfluxDB product you're using +

+ +
+
+
+ +
+ +
+
+ Do you know the URL of your InfluxDB server? +
+ + + + +
+ + +
+
+ Please enter your InfluxDB server URL: +
+
+ +
+ + +
+ + +
+
+ For airgapped environments, run this command from a machine that can + access your InfluxDB: +
+
curl -I http://your-influxdb-url:8086/ping
+
+ Then paste the response headers here: +
+ +
+ + +
+
+ + +
+
+ For Docker/Kubernetes environments, run these commands to identify your InfluxDB version: +
+
+ First, find your container: +
+
docker ps | grep influx
+
+ Then run one of these commands (replace <container> with your container name/ID): +
+
# Get version info: +docker exec <container> influxd version + +# Get ping headers: +docker exec <container> curl -I localhost:8086/ping + +# Or check startup logs: +docker logs <container> 2>&1 | head -20
+
+ Paste the output here: +
+ +
+ + +
+
+ + +
+
+ Which type of InfluxDB license do you have? +
+ + + + +
+ + +
+
+ Is your InfluxDB instance hosted by InfluxData (cloud) or + self-hosted? +
+ + + + +
+ + +
+
How long has your InfluxDB server been in place?
+ + + + + +
+ + +
+
Which query language(s) do you use with InfluxDB?
+ + + + + + +
+
+ +
+ + +
+ `; + // Cache DOM elements + this.progressBar = this.container.querySelector('#progress-bar'); + this.resultDiv = this.container.querySelector('#result'); + this.restartBtn = this.container.querySelector('#restart-btn'); + } + attachEventListeners() { + this.container.addEventListener('click', (e) => { + const target = e.target; + if (target.classList.contains('option-button') || + target.classList.contains('submit-button') || + target.classList.contains('back-button')) { + const action = target.dataset.action; + switch (action) { + case 'url-known': + this.trackAnalyticsEvent({ + interaction_type: 'question_answered', + question_id: 'url-known', + answer_value: target.dataset.value || '', + section: this.getCurrentPageSection(), + }); + this.handleUrlKnown(target.dataset.value); + break; + case 'go-back': + this.trackAnalyticsEvent({ + interaction_type: 'navigation', + section: this.getCurrentPageSection(), + }); + this.goBack(); + break; + case 'detect-url': + this.trackAnalyticsEvent({ + interaction_type: 'url_detection_attempt', + detection_method: 'url_analysis', + section: this.getCurrentPageSection(), + }); + this.detectByUrl(); + break; + case 'analyze-headers': + this.trackAnalyticsEvent({ + interaction_type: 'manual_analysis', + detection_method: 'ping_headers', + section: this.getCurrentPageSection(), + }); + this.analyzePingHeaders(); + break; + case 'analyze-docker': + this.trackAnalyticsEvent({ + interaction_type: 'manual_analysis', + detection_method: 'docker_output', + section: this.getCurrentPageSection(), + }); + this.analyzeDockerOutput(); + break; + case 'answer': + this.trackAnalyticsEvent({ + interaction_type: 'question_answered', + question_id: target.dataset.category || '', + answer_value: target.dataset.value || '', + section: this.getCurrentPageSection(), + }); + this.answerQuestion(target.dataset.category, target.dataset.value); + break; + case 'auth-help-answer': + this.trackAnalyticsEvent({ + interaction_type: 'auth_help_response', + question_id: target.dataset.category || '', + answer_value: target.dataset.value || '', + section: this.getCurrentPageSection(), + }); + this.handleAuthorizationHelp(target.dataset.category, target.dataset.value); + break; + case 'restart': + this.trackAnalyticsEvent({ + interaction_type: 'restart', + section: this.getCurrentPageSection(), + }); + this.restart(); + break; + case 'start-questionnaire': { + this.trackAnalyticsEvent({ + interaction_type: 'start_questionnaire', + section: this.getCurrentPageSection(), + }); + // Hide result and restart button first + if (this.resultDiv) { + this.resultDiv.classList.remove('show'); + } + if (this.restartBtn) { + this.restartBtn.style.display = 'none'; + } + // Start questionnaire with the detected context + this.startQuestionnaire(target.dataset.context || null); + // Focus on the component heading + const heading = document.getElementById('detector-title'); + if (heading) { + heading.focus(); + heading.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + break; + } + } + } + }); + } + updateProgress() { + const totalQuestions = this.questionFlow.length || 5; + const progress = ((this.currentQuestionIndex + 1) / totalQuestions) * 100; + if (this.progressBar) { + this.progressBar.style.width = `${progress}%`; + } + } + showQuestion(questionId, addToHistory = true) { + const questions = this.container.querySelectorAll('.question'); + questions.forEach((q) => q.classList.remove('active')); + const activeQuestion = this.container.querySelector(`#${questionId}`); + if (activeQuestion) { + activeQuestion.classList.add('active'); + // Add smart suggestions for URL input question + if (questionId === 'q-url-input') { + this.enhanceUrlInputWithSuggestions(); + } + } + // Track question history for back navigation + if (addToHistory) { + this.questionHistory.push(questionId); + } + this.updateProgress(); + } + enhanceUrlInputWithSuggestions() { + const urlInputQuestion = this.container.querySelector('#q-url-input'); + if (!urlInputQuestion) + return; + const urlInput = urlInputQuestion.querySelector('#url-input'); + if (!urlInput) + return; + // Check for existing URL in localStorage + const storedUrls = getInfluxDBUrls(); + const currentProduct = this.getCurrentProduct(); + const storedUrl = storedUrls[currentProduct] || storedUrls.custom; + if (storedUrl && storedUrl !== 'http://localhost:8086') { + urlInput.value = storedUrl; + // Add indicator that URL was pre-filled (only if one doesn't already exist) + const existingIndicator = urlInput.parentElement?.querySelector('.url-prefilled-indicator'); + if (!existingIndicator) { + const indicator = document.createElement('div'); + indicator.className = 'url-prefilled-indicator'; + indicator.textContent = 'Using previously saved URL'; + urlInput.parentElement?.insertBefore(indicator, urlInput); + // Hide indicator when user starts typing + const originalValue = urlInput.value; + urlInput.addEventListener('input', () => { + if (urlInput.value !== originalValue) { + indicator.style.display = 'none'; + } + }); + } + } + else { + // Set a basic placeholder suggestion + const suggestedUrl = this.getBasicUrlSuggestion(); + urlInput.placeholder = `for example, ${suggestedUrl}`; + } + } + getCurrentProduct() { + // Try to determine current product context from page or default + // This could be enhanced to detect from page context + return 'core'; // Default to core for now + } + handleUrlKnown(value) { + this.currentQuestionIndex++; + if (value === 'true') { + this.showQuestion('q-url-input'); + } + else if (value === 'airgapped') { + this.showQuestion('q-ping-manual'); + // Set up placeholder after question is shown + setTimeout(() => this.setupPingHeadersPlaceholder(), 0); + } + else if (value === 'docker') { + this.answers.isDocker = true; + this.showQuestion('q-docker-manual'); + // Set up placeholder after question is shown + setTimeout(() => this.setupDockerOutputPlaceholder(), 0); + } + else { + // Start the questionnaire + this.answers = {}; + this.questionFlow = ['q-paid', 'q-hosted', 'q-age', 'q-language']; + this.currentQuestionIndex = 0; + this.showQuestion('q-paid'); + } + } + goBack() { + // Remove current question from history + if (this.questionHistory.length > 0) { + this.questionHistory.pop(); + } + // Go to previous question if available + if (this.questionHistory.length > 0) { + const previousQuestion = this.questionHistory[this.questionHistory.length - 1]; + // Remove it from history before showing (showQuestion will re-add it) + this.questionHistory.pop(); + // Decrement question index + if (this.currentQuestionIndex > 0) { + this.currentQuestionIndex--; + } + // Show previous question + this.showQuestion(previousQuestion); + } + else { + // No history - go to first question + this.currentQuestionIndex = 0; + this.showQuestion('q-url-known'); + } + } + async detectByUrl() { + const urlInput = this.container.querySelector('#url-input')?.value.trim(); + if (!urlInput) { + this.showResult('error', 'Please enter a valid URL'); + return; + } + // Use improved URL pattern analysis + const analysisResult = this.analyzeUrlPatterns(urlInput); + // Store URL detection results for scoring system + if (analysisResult.likelyProduct && analysisResult.likelyProduct !== null) { + this.answers.detectedProduct = analysisResult.likelyProduct; + this.answers.detectedConfidence = analysisResult.confidence.toString(); + } + if (analysisResult.likelyProduct && analysisResult.likelyProduct !== null) { + if (analysisResult.suggestion === 'ping-test') { + // Show ping test suggestion for Core/Enterprise detection + this.showPingTestSuggestion(urlInput, analysisResult.likelyProduct); + return; + } + else if (analysisResult.suggestion === 'version-check') { + // Show OSS version check suggestion + this.showOSSVersionCheckSuggestion(urlInput); + return; + } + else if (analysisResult.suggestion === 'multiple-candidates-8086') { + // Show multiple product suggestions for port 8086 + this.showMultipleCandidatesSuggestion(urlInput, '8086'); + return; + } + else if (analysisResult.suggestion === 'multiple-candidates-8181') { + // Show multiple product suggestions for port 8181 + this.showMultipleCandidatesSuggestion(urlInput, '8181'); + return; + } + else { + // Direct detection + this.showDetectedVersion(analysisResult.likelyProduct); + return; + } + } + // URL not recognized - start questionnaire with context + this.showResult('info', 'Analyzing your InfluxDB server...'); + // Check if this is a cloud context (like "cloud 2") + const contextResult = this.detectContext(urlInput); + if (contextResult.likelyProduct === 'cloud') { + // Start questionnaire with cloud context + setTimeout(() => { + this.startQuestionnaireWithCloudContext(); + }, 2000); + } + else { + // For other URLs, use the regular questionnaire + setTimeout(() => { + this.startQuestionnaire('manual', this.detectPortFromUrl(urlInput)); + }, 2000); + } + } + detectContext(urlInput) { + const input = urlInput.toLowerCase(); + // Check for cloud indicators + if (input.includes('cloud') || input.includes('influxdata.com')) { + return { likelyProduct: 'cloud' }; + } + // Check for other patterns like "cloud 2" + if (/cloud\s*[v]?2/.test(input)) { + return { likelyProduct: 'cloud' }; + } + return {}; + } + detectPortFromUrl(urlString) { + try { + const url = new URL(urlString); + const port = url.port || (url.protocol === 'https:' ? '443' : '80'); + if (port === '8181') { + return 'v3'; // InfluxDB 3 Core/Enterprise typically use 8181 + } + else if (port === '8086') { + return 'legacy'; // OSS v1/v2 or Enterprise v1 typically use 8086 + } + } + catch { + // Invalid URL + } + return null; + } + startQuestionnaire(context = null, portClue = null) { + this.answers = {}; + this.answers.context = context; + this.answers.portClue = portClue; + this.answers.isCloud = false; + this.questionFlow = ['q-paid', 'q-age', 'q-language']; + this.currentQuestionIndex = 0; + this.showQuestion('q-paid'); + } + startQuestionnaireWithCloudContext() { + this.answers = {}; + this.answers.context = 'cloud'; + this.answers.hosted = 'cloud'; // Pre-set cloud hosting + this.answers.isCloud = true; + this.questionFlow = ['q-paid', 'q-age', 'q-language']; + this.currentQuestionIndex = 0; + this.showQuestion('q-paid'); + } + answerQuestion(category, answer) { + this.answers[category] = answer; + // Determine next question or show results + if (category === 'paid') { + if (!this.answers.context) { + // No URL provided - ask about cloud vs self-hosted + this.currentQuestionIndex = 1; + this.showQuestion('q-hosted'); + } + else { + // We have context from URL - go to age + this.currentQuestionIndex = 1; + this.showQuestion('q-age'); + } + } + else if (category === 'hosted') { + this.currentQuestionIndex = 2; + this.showQuestion('q-age'); + } + else if (category === 'age') { + this.currentQuestionIndex = 3; + this.showQuestion('q-language'); + } + else if (category === 'language') { + // All questions answered - show ranked results + this.showRankedResults(); + } + } + handleAuthorizationHelp(category, answer) { + // Store the answer + this.answers[category] = answer; + // Check if we're in the context of localhost:8181 detection + // If so, we can provide a high-confidence result + const currentUrl = this.container.querySelector('#url-input')?.value?.toLowerCase() || ''; + const isLocalhost8181 = (currentUrl.includes('localhost') || currentUrl.includes('127.0.0.1')) && + currentUrl.includes(':8181'); + if (isLocalhost8181) { + // For localhost:8181, we can give high-confidence results based on license + if (answer === 'free') { + // High confidence it's InfluxDB 3 Core + const html = ` + Based on your localhost:8181 server and free license:

+ ${this.generateProductResult('core', true, 'High', false)} +
+ Want to confirm this result? + +
+ `; + this.showResult('success', html); + } + else if (answer === 'paid') { + // High confidence it's InfluxDB 3 Enterprise + const html = ` + Based on your localhost:8181 server and paid license:

+ ${this.generateProductResult('enterprise', true, 'High', false)} +
+ Want to confirm this result? + +
+ `; + this.showResult('success', html); + } + } + else { + // Original behavior for non-localhost:8181 cases + const resultDiv = this.container.querySelector('#result'); + if (resultDiv) { + // Add a message about what the license answer means + const licenseGuidance = document.createElement('div'); + licenseGuidance.className = 'license-guidance'; + licenseGuidance.style.marginTop = '1rem'; + licenseGuidance.style.padding = '0.75rem'; + licenseGuidance.style.backgroundColor = + 'rgba(var(--article-link-rgb, 0, 163, 255), 0.1)'; + licenseGuidance.style.borderLeft = + '4px solid var(--article-link, #00A3FF)'; + licenseGuidance.style.borderRadius = '4px'; + if (answer === 'free') { + licenseGuidance.innerHTML = ` + Free/Open Source License: +

This suggests you're using InfluxDB 3 Core or InfluxDB OSS.

+ + `; + } + else if (answer === 'paid') { + licenseGuidance.innerHTML = ` + Paid/Commercial License: +

This suggests you're using InfluxDB 3 Enterprise or a paid cloud service.

+ + `; + } + // Remove any existing guidance + const existingGuidance = resultDiv.querySelector('.license-guidance'); + if (existingGuidance) { + existingGuidance.remove(); + } + // Add the new guidance + resultDiv.appendChild(licenseGuidance); + // Focus on the guidance message for accessibility + licenseGuidance.focus(); + } + } + } + showRankedResults() { + const scores = {}; + // Initialize all products with base score using their full display names + // The scoring logic uses full names like 'InfluxDB 3 Core', not keys like 'influxdb3_core' + Object.entries(this.products).forEach(([key, config]) => { + const fullName = config.name || key; + scores[fullName] = 0; + }); + // Apply scoring logic based on answers + this.applyScoring(scores); + // Check if user answered "unknown" to all questions + const allUnknown = (!this.answers.paid || this.answers.paid === 'unknown') && + (!this.answers.hosted || this.answers.hosted === 'unknown') && + (!this.answers.age || this.answers.age === 'unknown') && + (!this.answers.language || this.answers.language === 'unknown'); + // Sort by score and filter out vague products + const ranked = Object.entries(scores) + .filter(([product, score]) => { + // Filter by score threshold + if (score <= -50) + return false; + // Exclude generic "InfluxDB" product (too vague for results) + if (product === 'InfluxDB') + return false; + return true; + }) + .sort((a, b) => b[1] - a[1]) + .slice(0, 5); + // Display results + this.displayRankedResults(ranked, allUnknown); + } + /** + * Gets the Grafana documentation link for a given product + */ + getGrafanaLink(productName) { + const GRAFANA_LINKS = { + 'InfluxDB 3 Core': '/influxdb3/core/visualize-data/grafana/', + 'InfluxDB 3 Enterprise': '/influxdb3/enterprise/visualize-data/grafana/', + 'InfluxDB Cloud Dedicated': '/influxdb3/cloud-dedicated/visualize-data/grafana/', + 'InfluxDB Cloud Serverless': '/influxdb3/cloud-serverless/visualize-data/grafana/', + 'InfluxDB OSS 1.x': '/influxdb/v1/tools/grafana/', + 'InfluxDB OSS 2.x': '/influxdb/v2/visualize-data/grafana/', + 'InfluxDB Enterprise': '/influxdb/enterprise/visualize-data/grafana/', + 'InfluxDB Clustered': '/influxdb3/clustered/visualize-data/grafana/', + 'InfluxDB Cloud (TSM)': '/influxdb/cloud/visualize-data/grafana/', + 'InfluxDB Cloud v1': '/influxdb/cloud/visualize-data/grafana/', + }; + return GRAFANA_LINKS[productName] || null; + } + /** + * Generates a unified product result block with characteristics and Grafana link + */ + generateProductResult(productName, isTopResult = false, confidence, showRanking) { + const displayName = this.getProductDisplayName(productName) || productName; + const grafanaLink = this.getGrafanaLink(displayName); + const resultClass = isTopResult + ? 'product-ranking top-result' + : 'product-ranking'; + // Get characteristics from products data + const characteristics = this.products[productName]?.characteristics; + let html = `
`; + if (showRanking) { + html += `
${displayName}
`; + if (isTopResult) { + html += 'Most Likely'; + } + } + else { + html += `
${displayName}
`; + if (isTopResult) { + html += 'Detected'; + } + } + // Add characteristics and confidence + const details = []; + if (confidence) + details.push(`Confidence: ${confidence}`); + if (characteristics) { + details.push(characteristics.slice(0, 3).join(', ')); + } + if (details.length > 0) { + html += `
${details.join(' • ')}
`; + } + // Add Grafana link if available + if (grafanaLink) { + html += ` + + `; + } + html += '
'; + // Add configuration guidance for top results + if (isTopResult) { + const configGuidance = this.generateConfigurationGuidance(productName); + if (configGuidance) { + html += configGuidance; + } + } + return html; + } + /** + * Maps simple product keys (used in URL detection) to full product names (used in scoring) + */ + mapProductKeyToFullName(productKey) { + const KEY_TO_FULL_NAME_MAP = { + core: 'InfluxDB 3 Core', + enterprise: 'InfluxDB 3 Enterprise', + serverless: 'InfluxDB Cloud Serverless', + dedicated: 'InfluxDB Cloud Dedicated', + clustered: 'InfluxDB Clustered', + 'cloud-v2-tsm': 'InfluxDB Cloud (TSM)', + 'cloud-v1': 'InfluxDB Cloud v1', + oss: 'InfluxDB OSS 2.x', + 'oss-1x': 'InfluxDB OSS 1.x', + 'enterprise-1x': 'InfluxDB Enterprise', + }; + return KEY_TO_FULL_NAME_MAP[productKey] || null; + } + applyScoring(scores) { + // Product release dates for time-aware scoring + const PRODUCT_RELEASE_DATES = { + 'InfluxDB 3 Core': new Date('2025-01-01'), + 'InfluxDB 3 Enterprise': new Date('2025-01-01'), + 'InfluxDB Cloud Serverless': new Date('2024-01-01'), + 'InfluxDB Cloud Dedicated': new Date('2024-01-01'), + 'InfluxDB Clustered': new Date('2024-01-01'), + 'InfluxDB OSS 2.x': new Date('2020-11-01'), + 'InfluxDB Cloud (TSM)': new Date('2020-11-01'), + 'InfluxDB OSS 1.x': new Date('2016-09-01'), + 'InfluxDB Enterprise': new Date('2016-09-01'), + }; + const currentDate = new Date(); + // Apply URL detection boost if available + if (this.answers.detectedProduct && this.answers.detectedConfidence) { + const detectedProduct = this.answers.detectedProduct; + const confidence = typeof this.answers.detectedConfidence === 'number' + ? this.answers.detectedConfidence + : parseFloat(this.answers.detectedConfidence); + // Determine confidence boost value + let boostValue = 0; + if (confidence >= 1.0) { + boostValue = 100; // Definitive match + } + else if (confidence >= 0.9) { + boostValue = 80; // Very high confidence + } + else if (confidence >= 0.7) { + boostValue = 60; // High confidence + } + else if (confidence >= 0.5) { + boostValue = 40; // Medium confidence + } + // Handle special case: 'core or enterprise' should boost BOTH products equally + if (detectedProduct === 'core or enterprise') { + scores['InfluxDB 3 Core'] += boostValue; + scores['InfluxDB 3 Enterprise'] += boostValue; + } + else { + // Normal case: boost single detected product + const fullProductName = this.mapProductKeyToFullName(detectedProduct); + if (fullProductName && scores[fullProductName] !== undefined) { + scores[fullProductName] += boostValue; + } + } + } + // Cloud vs self-hosted + if (this.answers.hosted === 'cloud') { + scores['InfluxDB 3 Core'] = -1000; + scores['InfluxDB 3 Enterprise'] = -1000; + scores['InfluxDB OSS 1.x'] = -1000; + scores['InfluxDB OSS 2.x'] = -1000; + scores['InfluxDB Enterprise'] = -1000; + scores['InfluxDB Clustered'] = -1000; + } + else if (this.answers.hosted === 'self' || !this.answers.isCloud) { + scores['InfluxDB Cloud Dedicated'] = -1000; + scores['InfluxDB Cloud Serverless'] = -1000; + scores['InfluxDB Cloud (TSM)'] = -1000; + } + // Paid vs Free + if (this.answers.paid === 'free') { + scores['InfluxDB 3 Core'] += 25; + scores['InfluxDB OSS 1.x'] += 25; + scores['InfluxDB OSS 2.x'] += 25; + scores['InfluxDB'] += 25; // Generic InfluxDB (OSS v2.x) + scores['InfluxDB Cloud Serverless'] += 10; + scores['InfluxDB Cloud (TSM)'] += 10; + scores['InfluxDB 3 Enterprise'] = -1000; + scores['InfluxDB Enterprise'] = -1000; + scores['InfluxDB Clustered'] = -1000; + scores['InfluxDB Cloud Dedicated'] = -1000; + } + else if (this.answers.paid === 'paid') { + scores['InfluxDB 3 Enterprise'] += 25; + scores['InfluxDB Enterprise'] += 20; + scores['InfluxDB Clustered'] += 15; + scores['InfluxDB Cloud Dedicated'] += 20; + scores['InfluxDB Cloud Serverless'] += 15; + scores['InfluxDB Cloud (TSM)'] += 15; + scores['InfluxDB 3 Core'] = -1000; + scores['InfluxDB OSS 1.x'] = -1000; + scores['InfluxDB OSS 2.x'] = -1000; + scores['InfluxDB'] = -1000; // Generic InfluxDB (OSS v2.x) + } + // Time-aware age-based scoring + Object.entries(scores).forEach(([product]) => { + const releaseDate = PRODUCT_RELEASE_DATES[product]; + if (!releaseDate) + return; + const yearsSinceRelease = (currentDate.getTime() - releaseDate.getTime()) / + (365.25 * 24 * 60 * 60 * 1000); + if (this.answers.age === 'recent') { + // Favor products released within last year + if (yearsSinceRelease < 1) { + scores[product] += 40; // Very new product + } + else if (yearsSinceRelease < 3) { + scores[product] += 25; // Relatively new + } + } + else if (this.answers.age === '1-5') { + // Check if product existed in this timeframe + if (yearsSinceRelease >= 1 && yearsSinceRelease <= 5) { + scores[product] += 25; + } + else if (yearsSinceRelease < 1) { + scores[product] -= 30; // Too new for this age range + } + } + else if (this.answers.age === '5+') { + // Only penalize if product didn't exist 5+ years ago + if (yearsSinceRelease < 5) { + scores[product] -= 100; // Product didn't exist 5 years ago + } + else { + scores[product] += 30; // Product was available 5+ years ago + } + } + }); + // Query language scoring + if (this.answers.language === 'sql') { + scores['InfluxDB 3 Core'] += 40; + scores['InfluxDB 3 Enterprise'] += 40; + scores['InfluxDB Cloud Dedicated'] += 30; + scores['InfluxDB Cloud Serverless'] += 30; + scores['InfluxDB Clustered'] += 30; + scores['InfluxDB OSS 1.x'] = -1000; + scores['InfluxDB OSS 2.x'] = -1000; + scores['InfluxDB'] = -1000; // Generic InfluxDB (OSS v2.x) + scores['InfluxDB Enterprise'] = -1000; + scores['InfluxDB Cloud (TSM)'] = -1000; + } + else if (this.answers.language === 'flux') { + scores['InfluxDB OSS 2.x'] += 30; + scores['InfluxDB'] += 30; // Generic InfluxDB (OSS v2.x) + scores['InfluxDB Cloud (TSM)'] += 40; + scores['InfluxDB Cloud Serverless'] += 20; + scores['InfluxDB Enterprise'] += 20; // v1.x Enterprise supports Flux + scores['InfluxDB OSS 1.x'] = -1000; + scores['InfluxDB 3 Core'] = -1000; + scores['InfluxDB 3 Enterprise'] = -1000; + scores['InfluxDB Cloud Dedicated'] = -1000; + scores['InfluxDB Clustered'] = -1000; + } + else if (this.answers.language === 'influxql') { + // InfluxQL is supported by all products except pure Flux products + scores['InfluxDB OSS 1.x'] += 30; + scores['InfluxDB Enterprise'] += 30; + scores['InfluxDB OSS 2.x'] += 20; + scores['InfluxDB'] += 20; // Generic InfluxDB (OSS v2.x) + scores['InfluxDB Cloud (TSM)'] += 20; + scores['InfluxDB 3 Core'] += 25; + scores['InfluxDB 3 Enterprise'] += 25; + scores['InfluxDB Cloud Dedicated'] += 25; + scores['InfluxDB Cloud Serverless'] += 25; + scores['InfluxDB Clustered'] += 25; + } + } + displayRankedResults(ranked, allUnknown = false) { + const topScore = ranked[0]?.[1] || 0; + const secondScore = ranked[1]?.[1] || 0; + const hasStandout = topScore > 30 && topScore - secondScore >= 15; + let html = ''; + // If all answers were "I'm not sure", show a helpful message + if (allUnknown) { + html = + 'Unable to determine your InfluxDB product

' + + '

Since you answered "I\'m not sure" to all questions, we don\'t have enough information to identify your InfluxDB product.

' + + '

Please check the InfluxDB version quick reference table below to identify your product based on its characteristics.


'; + } + else { + html = + 'Based on your answers, here are the most likely InfluxDB products:

'; + } + // Only show ranked products if we have meaningful answers + if (!allUnknown) { + ranked.forEach(([product, score], index) => { + const confidence = score > 60 ? 'High' : score > 30 ? 'Medium' : 'Low'; + const isTopResult = index === 0 && hasStandout; + // Use unified product result generation with ranking number + let productHtml = this.generateProductResult(product, isTopResult, confidence, true); + // Add ranking number to the product title + productHtml = productHtml.replace('
', `
${index + 1}. `); + html += productHtml; + }); + } + // Add Quick Reference table (open by default if all answers unknown) + html += ` +
+ + + InfluxDB version quick reference + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProductLicenseHostingPortPing requires authQuery languages
InfluxDB 3 EnterprisePaid onlySelf-hosted8181Yes (opt-out)SQL, InfluxQL
InfluxDB 3 CoreFree onlySelf-hosted8181Yes (opt-out)SQL, InfluxQL
InfluxDB EnterprisePaid onlySelf-hosted8086Yes (required)InfluxQL, Flux
InfluxDB ClusteredPaid onlySelf-hostedVariesNoSQL, InfluxQL
InfluxDB OSS 1.xFree onlySelf-hosted8086No (optional)InfluxQL
InfluxDB OSS 2.xFree onlySelf-hosted8086NoInfluxQL, Flux
InfluxDB Cloud DedicatedPaid onlyCloudN/ANoSQL, InfluxQL
InfluxDB Cloud ServerlessFree + PaidCloudN/AN/ASQL, InfluxQL, Flux
InfluxDB Cloud (TSM)Free + PaidCloudN/AN/AInfluxQL, Flux
+ +
+ `; + this.showResult('success', html); + } + analyzePingHeaders() { + const headersText = this.container.querySelector('#ping-headers')?.value.trim(); + if (!headersText) { + this.showResult('error', 'Please paste the ping response headers'); + return; + } + // Check if user is trying to analyze the example content + if (headersText.includes('# Replace this with your actual response headers') || + headersText.includes('# Example formats:')) { + this.showResult('error', 'Please replace the example content with your actual ping response headers'); + return; + } + // Check for 401/403 unauthorized responses + if (headersText.includes('401') || headersText.includes('403')) { + this.showResult('info', ` + Authentication Required Detected

+ The ping endpoint requires authentication, which indicates you're likely using one of:

+
+
+ InfluxDB 3 Enterprise - Requires auth by default (opt-out possible) +
+
+ InfluxDB 3 Core - Requires auth by default (opt-out possible) +
+
+ Please use the guided questions to narrow down your specific version. + `); + return; + } + // Parse headers and check against patterns + const headers = {}; + headersText.split('\n').forEach((line) => { + const colonIndex = line.indexOf(':'); + if (colonIndex > -1) { + const key = line.substring(0, colonIndex).trim().toLowerCase(); + const value = line.substring(colonIndex + 1).trim(); + headers[key] = value; + } + }); + // PRIORITY: Check for definitive x-influxdb-build header (per decision tree) + const buildHeader = headers['x-influxdb-build']; + if (buildHeader) { + if (buildHeader.toLowerCase().includes('enterprise')) { + this.showDetectedVersion('InfluxDB 3 Enterprise'); + return; + } + else if (buildHeader.toLowerCase().includes('core')) { + this.showDetectedVersion('InfluxDB 3 Core'); + return; + } + } + // Check against product patterns + let detectedProduct = null; + for (const [productName, config] of Object.entries(this.products)) { + if (config.detection?.ping_headers) { + let matches = true; + for (const [header, pattern] of Object.entries(config.detection.ping_headers)) { + const regex = new RegExp(pattern); + if (!headers[header] || !regex.test(headers[header])) { + matches = false; + break; + } + } + if (matches) { + detectedProduct = productName; + break; + } + } + } + if (detectedProduct) { + this.showDetectedVersion(detectedProduct); + } + else { + this.showResult('warning', 'Unable to determine version from headers. Consider using the guided questions instead.'); + } + } + showResult(type, message) { + if (this.resultDiv) { + this.resultDiv.className = `result ${type} show`; + this.resultDiv.innerHTML = message; + } + if (this.restartBtn) { + this.restartBtn.style.display = 'block'; + } + } + analyzeDockerOutput() { + const dockerOutput = this.container.querySelector('#docker-output')?.value.trim(); + if (!dockerOutput) { + this.showResult('error', 'Please paste the Docker command output'); + return; + } + // Check if user is trying to analyze the example content + if (dockerOutput.includes('# Replace this with your actual command output') || + dockerOutput.includes('# Example formats:')) { + this.showResult('error', 'Please replace the example content with your actual Docker command output'); + return; + } + let detectedProduct = null; + // Check for version patterns in the output + if (dockerOutput.includes('InfluxDB 3 Core')) { + detectedProduct = 'InfluxDB 3 Core'; + } + else if (dockerOutput.includes('InfluxDB 3 Enterprise')) { + detectedProduct = 'InfluxDB 3 Enterprise'; + } + else if (dockerOutput.includes('InfluxDB v3')) { + // Generic v3 detection - need more info + detectedProduct = 'InfluxDB 3 Core or Enterprise'; + } + else if (dockerOutput.includes('InfluxDB v2') || + dockerOutput.includes('InfluxDB 2.')) { + detectedProduct = 'InfluxDB OSS 2.x'; + } + else if (dockerOutput.includes('InfluxDB v1') || + dockerOutput.includes('InfluxDB 1.')) { + if (dockerOutput.includes('Enterprise')) { + detectedProduct = 'InfluxDB Enterprise'; + } + else { + detectedProduct = 'InfluxDB OSS 1.x'; + } + } + // Also check for ping header patterns (case-insensitive) + if (!detectedProduct) { + // First check for x-influxdb-build header (definitive identification) + const buildMatch = dockerOutput.match(/x-influxdb-build:\s*(\w+)/i); + if (buildMatch) { + const build = buildMatch[1].toLowerCase(); + if (build === 'enterprise') { + detectedProduct = 'InfluxDB 3 Enterprise'; + } + else if (build === 'core') { + detectedProduct = 'InfluxDB 3 Core'; + } + } + // If no build header, check version headers (case-insensitive) + if (!detectedProduct) { + const versionMatch = dockerOutput.match(/x-influxdb-version:\s*([\d.]+)/i); + if (versionMatch) { + const version = versionMatch[1]; + if (version.startsWith('3.')) { + detectedProduct = 'InfluxDB 3 Core or InfluxDB 3Enterprise'; + } + else if (version.startsWith('2.')) { + detectedProduct = 'InfluxDB OSS 2.x'; + } + else if (version.startsWith('1.')) { + detectedProduct = dockerOutput.includes('Enterprise') + ? 'InfluxDB Enterprise' + : 'InfluxDB OSS 1.x'; + } + } + } + } + if (detectedProduct) { + this.showDetectedVersion(detectedProduct); + } + else { + this.showResult('warning', 'Unable to determine version from Docker output. Consider using the guided questions instead.'); + } + } + showPingTestSuggestion(url, productName) { + // Convert product key to display name + const displayName = this.getProductDisplayName(productName) || productName; + const html = ` + Port 8181 detected - likely ${displayName}

+ +

To distinguish between InfluxDB 3 Core and Enterprise, run one of these commands:

+ +
+# Direct API call: +curl -I ${url}/ping +
+ +
+ + View Docker/Container Commands + +
+# With Docker Compose: +docker compose exec influxdb3 curl -I http://localhost:8181/ping + +# With Docker (replace <container> with your container name): +docker exec <container> curl -I localhost:8181/ping +
+
+ +
+
Expected results:
+ • X-Influxdb-Build: Enterprise → InfluxDB 3 Enterprise (definitive)
+ • X-Influxdb-Build: Core → InfluxDB 3 Core (definitive)
+ • 401 Unauthorized → Use the license information below +
+ +
+
If you get 401 Unauthorized:
+

What type of license do you have?

+ + +
+ +
+ Can't run the command? + +
+ `; + this.showResult('success', html); + } + showOSSVersionCheckSuggestion(url) { + const html = ` + Port 8086 detected - likely InfluxDB OSS

+ +

To determine if this is InfluxDB OSS v1.x or v2.x, run one of these commands:

+ +
+# Check version directly: +influxd version + +# Or check via API: +curl -I ${url}/ping +
+ +
+
Expected version patterns:
+ • v1.x.x → ${this.getProductDisplayName('oss-v1')}
+ • v2.x.x → ${this.getProductDisplayName('oss-v2')}
+
+ +
+ + Docker/Container Commands + +
+# Get version info: +docker exec <container> influxd version + +# Get ping headers: +docker exec <container> curl -I localhost:8086/ping + +# Or check startup logs: +docker logs <container> 2>&1 | head -20 +
+

+ Replace <container> with your actual container name or ID. +

+
+ +
+ Can't run these commands? + +
+ `; + this.showResult('success', html); + } + showMultipleCandidatesSuggestion(url, port) { + let candidates = []; + let portDescription = ''; + if (port === '8086') { + candidates = [ + 'InfluxDB OSS 1.x', + 'InfluxDB OSS 2.x', + 'InfluxDB Enterprise', + ]; + portDescription = + 'Port 8086 is used by InfluxDB OSS v1.x, OSS v2.x, and Enterprise v1.x'; + } + else if (port === '8181') { + candidates = ['InfluxDB 3 Core', 'InfluxDB 3 Enterprise']; + portDescription = 'Port 8181 is used by InfluxDB 3 Core and Enterprise'; + } + const candidatesList = candidates + .map((product) => this.generateProductResult(product, false, 'Medium', false)) + .join(''); + const html = ` + Based on the port pattern in your URL, here are the possible products:

+ +

${portDescription}. Without additional information, we cannot determine which specific version you're using.

+ +
+ Possible products:
+ ${candidatesList} +
+ +
+ To narrow this down: + +
+ `; + this.showResult('info', html); + } + showDetectedVersion(productName) { + // Track successful detection + this.trackAnalyticsEvent({ + interaction_type: 'product_detected', + detected_product: productName.toLowerCase().replace(/\s+/g, '_'), + completion_status: 'success', + section: this.getCurrentPageSection(), + }); + const html = ` + Based on your input, we believe the InfluxDB product you are using is most likely:

+ ${this.generateProductResult(productName, true, 'High', false)} + `; + this.showResult('success', html); + } + restart() { + this.answers = {}; + this.questionFlow = []; + this.currentQuestionIndex = 0; + this.questionHistory = []; + // Clear inputs + const urlInput = this.container.querySelector('#url-input'); + const pingHeaders = this.container.querySelector('#ping-headers'); + const dockerOutput = this.container.querySelector('#docker-output'); + if (urlInput) + urlInput.value = ''; + if (pingHeaders) + pingHeaders.value = ''; + if (dockerOutput) + dockerOutput.value = ''; + // Remove URL prefilled indicator if present + const indicator = this.container.querySelector('.url-prefilled-indicator'); + if (indicator) { + indicator.remove(); + } + // Hide result + if (this.resultDiv) { + this.resultDiv.classList.remove('show'); + } + if (this.restartBtn) { + this.restartBtn.style.display = 'none'; + } + // Show first question + this.showQuestion('q-url-known'); + // Reset progress + if (this.progressBar) { + this.progressBar.style.width = '0%'; + } + } +} +// Export as component initializer +export default function initInfluxDBVersionDetector(options) { + return new InfluxDBVersionDetector(options); +} +//# sourceMappingURL=influxdb-version-detector.js.map \ No newline at end of file diff --git a/dist/influxdb-version-detector.js.map b/dist/influxdb-version-detector.js.map new file mode 100644 index 0000000000..a6955f2826 --- /dev/null +++ b/dist/influxdb-version-detector.js.map @@ -0,0 +1 @@ +{"version":3,"file":"influxdb-version-detector.js","sourceRoot":"","sources":["../assets/js/influxdb-version-detector.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/services/influxdb-urls.d.ts b/dist/services/influxdb-urls.d.ts new file mode 100644 index 0000000000..6c1aa5b7b8 --- /dev/null +++ b/dist/services/influxdb-urls.d.ts @@ -0,0 +1,2 @@ +export const influxdbUrls: any; +//# sourceMappingURL=influxdb-urls.d.ts.map \ No newline at end of file diff --git a/dist/services/influxdb-urls.d.ts.map b/dist/services/influxdb-urls.d.ts.map new file mode 100644 index 0000000000..cd0f13cb47 --- /dev/null +++ b/dist/services/influxdb-urls.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"influxdb-urls.d.ts","sourceRoot":"","sources":["../../assets/js/services/influxdb-urls.js"],"names":[],"mappings":"AAEA,+BAAoD"} \ No newline at end of file diff --git a/dist/services/influxdb-urls.js b/dist/services/influxdb-urls.js new file mode 100644 index 0000000000..c3ce86b388 --- /dev/null +++ b/dist/services/influxdb-urls.js @@ -0,0 +1,3 @@ +import { influxdb_urls as influxdbUrlsParam } from '@params'; +export const influxdbUrls = influxdbUrlsParam || {}; +//# sourceMappingURL=influxdb-urls.js.map \ No newline at end of file diff --git a/dist/services/influxdb-urls.js.map b/dist/services/influxdb-urls.js.map new file mode 100644 index 0000000000..05bb0a0e62 --- /dev/null +++ b/dist/services/influxdb-urls.js.map @@ -0,0 +1 @@ +{"version":3,"file":"influxdb-urls.js","sourceRoot":"","sources":["../../assets/js/services/influxdb-urls.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE7D,MAAM,CAAC,MAAM,YAAY,GAAG,iBAAiB,IAAI,EAAE,CAAC"} \ No newline at end of file diff --git a/dist/services/local-storage.d.ts b/dist/services/local-storage.d.ts new file mode 100644 index 0000000000..b0524ed880 --- /dev/null +++ b/dist/services/local-storage.d.ts @@ -0,0 +1,30 @@ +export namespace DEFAULT_STORAGE_URLS { + let oss: any; + let cloud: any; + let serverless: any; + let core: any; + let enterprise: any; + let dedicated: any; + let clustered: any; + let prev_oss: any; + let prev_cloud: any; + let prev_core: any; + let prev_enterprise: any; + let prev_serverless: any; + let prev_dedicated: any; + let prev_clustered: any; + let custom: string; +} +export const defaultUrls: {}; +export function initializeStorageItem(storageKey: any, defaultValue: any): void; +export function getPreference(prefName: any): any; +export function setPreference(prefID: any, prefValue: any): void; +export function getPreferences(): any; +export function getInfluxDBUrls(): any; +export function getInfluxDBUrl(product: any): any; +export function setInfluxDBUrls(updatedUrlsObj: any): void; +export function removeInfluxDBUrl(product: any): void; +export function getNotifications(): any; +export function notificationIsRead(notificationID: any, notificationType: any): any; +export function setNotificationAsRead(notificationID: any, notificationType: any): void; +//# sourceMappingURL=local-storage.d.ts.map \ No newline at end of file diff --git a/dist/services/local-storage.d.ts.map b/dist/services/local-storage.d.ts.map new file mode 100644 index 0000000000..3bc42d3cc1 --- /dev/null +++ b/dist/services/local-storage.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"local-storage.d.ts","sourceRoot":"","sources":["../../assets/js/services/local-storage.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAqFA,6BAAuB;AAhEvB,gFAOC;AAwBD,kDAYC;AAGD,iEAOC;AAGD,sCAEC;AAkCD,uCAOC;AAGD,kDAYC;AAOD,2DAOC;AAGD,sDAOC;AAgBD,wCAeC;AAYD,oFAKC;AAWD,wFAWC"} \ No newline at end of file diff --git a/dist/services/local-storage.js b/dist/services/local-storage.js new file mode 100644 index 0000000000..9e42ada09e --- /dev/null +++ b/dist/services/local-storage.js @@ -0,0 +1,187 @@ +/* + This represents an API for managing user and client-side settings for the + InfluxData documentation. It uses the local browser storage. + + These functions manage the following InfluxDB settings: + + - influxdata_docs_preferences: Docs UI/UX-related preferences (obj) + - influxdata_docs_urls: User-defined InfluxDB URLs for each product (obj) + - influxdata_docs_notifications: + - messages: Messages (data/notifications.yaml) that have been seen (array) + - callouts: Feature callouts that have been seen (array) +*/ +import { influxdbUrls } from './influxdb-urls.js'; +// Prefix for all InfluxData docs local storage +const storagePrefix = 'influxdata_docs_'; +/* + Initialize data in local storage with a default value. +*/ +function initializeStorageItem(storageKey, defaultValue) { + const fullStorageKey = storagePrefix + storageKey; + // Check if the data exists before initializing the data + if (localStorage.getItem(fullStorageKey) === null) { + localStorage.setItem(fullStorageKey, defaultValue); + } +} +/* +//////////////////////////////////////////////////////////////////////////////// +////////////////////////// INFLUXDATA DOCS PREFERENCES ///////////////////////// +//////////////////////////////////////////////////////////////////////////////// +*/ +const prefStorageKey = storagePrefix + 'preferences'; +// Default preferences +const defaultPrefObj = { + api_lib: null, + influxdb_url: 'cloud', + sidebar_state: 'open', + theme: 'light', + sample_get_started_date: null, + v3_wayfinding_show: true, +}; +/* + Retrieve a preference from the preference key. + If the key doesn't exist, initialize it with default values. +*/ +function getPreference(prefName) { + // Initialize preference data if it doesn't already exist + if (localStorage.getItem(prefStorageKey) === null) { + initializeStorageItem('preferences', JSON.stringify(defaultPrefObj)); + } + // Retrieve and parse preferences as JSON + const prefString = localStorage.getItem(prefStorageKey); + const prefObj = JSON.parse(prefString); + // Return the value of the specified preference + return prefObj[prefName]; +} +// Set a preference in the preferences key +function setPreference(prefID, prefValue) { + const prefString = localStorage.getItem(prefStorageKey); + const prefObj = JSON.parse(prefString); + prefObj[prefID] = prefValue; + localStorage.setItem(prefStorageKey, JSON.stringify(prefObj)); +} +// Return an object containing all preferences +function getPreferences() { + return JSON.parse(localStorage.getItem(prefStorageKey)); +} +//////////////////////////////////////////////////////////////////////////////// +//////////// MANAGE INFLUXDATA DOCS URLS IN LOCAL STORAGE ////////////////////// +//////////////////////////////////////////////////////////////////////////////// +const defaultUrls = {}; +Object.entries(influxdbUrls).forEach(([product, { providers }]) => { + defaultUrls[product] = + providers.filter((provider) => provider.name === 'Default')[0]?.regions[0] + ?.url || 'https://cloud2.influxdata.com'; +}); +export const DEFAULT_STORAGE_URLS = { + oss: defaultUrls.oss, + cloud: defaultUrls.cloud, + serverless: defaultUrls.serverless, + core: defaultUrls.core, + enterprise: defaultUrls.enterprise, + dedicated: defaultUrls.cloud_dedicated, + clustered: defaultUrls.clustered, + prev_oss: defaultUrls.oss, + prev_cloud: defaultUrls.cloud, + prev_core: defaultUrls.core, + prev_enterprise: defaultUrls.enterprise, + prev_serverless: defaultUrls.serverless, + prev_dedicated: defaultUrls.cloud_dedicated, + prev_clustered: defaultUrls.clustered, + custom: '', +}; +const urlStorageKey = storagePrefix + 'urls'; +// Return an object that contains all InfluxDB urls stored in the urls key +function getInfluxDBUrls() { + // Initialize urls data if it doesn't already exist + if (localStorage.getItem(urlStorageKey) === null) { + initializeStorageItem('urls', JSON.stringify(DEFAULT_STORAGE_URLS)); + } + return JSON.parse(localStorage.getItem(urlStorageKey)); +} +// Get the current or previous URL for a specific product or a custom url +function getInfluxDBUrl(product) { + // Initialize urls data if it doesn't already exist + if (localStorage.getItem(urlStorageKey) === null) { + initializeStorageItem('urls', JSON.stringify(DEFAULT_STORAGE_URLS)); + } + // Retrieve and parse the URLs as JSON + const urlsString = localStorage.getItem(urlStorageKey); + const urlsObj = JSON.parse(urlsString); + // Return the URL of the specified product + return urlsObj[product]; +} +/* + Set multiple product URLs in the urls key. + Input should be an object where the key is the product and the value is the + URL to set for that product. +*/ +function setInfluxDBUrls(updatedUrlsObj) { + const urlsString = localStorage.getItem(urlStorageKey); + const urlsObj = JSON.parse(urlsString); + const newUrlsObj = { ...urlsObj, ...updatedUrlsObj }; + localStorage.setItem(urlStorageKey, JSON.stringify(newUrlsObj)); +} +// Set an InfluxDB URL to an empty string in the urls key +function removeInfluxDBUrl(product) { + const urlsString = localStorage.getItem(urlStorageKey); + const urlsObj = JSON.parse(urlsString); + urlsObj[product] = ''; + localStorage.setItem(urlStorageKey, JSON.stringify(urlsObj)); +} +/* +//////////////////////////////////////////////////////////////////////////////// +///////////////////////// INFLUXDATA DOCS NOTIFICATIONS //////////////////////// +//////////////////////////////////////////////////////////////////////////////// +*/ +const notificationStorageKey = storagePrefix + 'notifications'; +// Default notifications +const defaultNotificationsObj = { + messages: [], + callouts: [], +}; +function getNotifications() { + // Initialize notifications data if it doesn't already exist + if (localStorage.getItem(notificationStorageKey) === null) { + initializeStorageItem('notifications', JSON.stringify(defaultNotificationsObj)); + } + // Retrieve and parse the notifications data as JSON + const notificationString = localStorage.getItem(notificationStorageKey); + const notificationObj = JSON.parse(notificationString); + // Return the notifications object + return notificationObj; +} +/* + Checks if a notification is read. Provide the notification ID and one of the + following notification types: + + - message + - callout + + If the notification ID exists in the array assigned to the specified type, the + notification has been read. +*/ +function notificationIsRead(notificationID, notificationType) { + const notificationsObj = getNotifications(); + const readNotifications = notificationsObj[`${notificationType}s`]; + return readNotifications.includes(notificationID); +} +/* + Sets a notification as read. Provide the notification ID and one of the + following notification types: + + - message + - callout + + The notification ID is added to the array assigned to the specified type. +*/ +function setNotificationAsRead(notificationID, notificationType) { + const notificationsObj = getNotifications(); + const readNotifications = notificationsObj[`${notificationType}s`]; + readNotifications.push(notificationID); + notificationsObj[notificationType + 's'] = readNotifications; + localStorage.setItem(notificationStorageKey, JSON.stringify(notificationsObj)); +} +// Export functions as a module and make the file backwards compatible for non-module environments until all remaining dependent scripts are ported to modules +export { defaultUrls, initializeStorageItem, getPreference, setPreference, getPreferences, getInfluxDBUrls, getInfluxDBUrl, setInfluxDBUrls, removeInfluxDBUrl, getNotifications, notificationIsRead, setNotificationAsRead, }; +//# sourceMappingURL=local-storage.js.map \ No newline at end of file diff --git a/dist/services/local-storage.js.map b/dist/services/local-storage.js.map new file mode 100644 index 0000000000..54c11dc98f --- /dev/null +++ b/dist/services/local-storage.js.map @@ -0,0 +1 @@ +{"version":3,"file":"local-storage.js","sourceRoot":"","sources":["../../assets/js/services/local-storage.js"],"names":[],"mappings":"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"} \ No newline at end of file From ad3b0e5fb38b27630423ce2b046a492150fc85f2 Mon Sep 17 00:00:00 2001 From: Jason Stirnaman Date: Thu, 2 Oct 2025 16:57:03 -0500 Subject: [PATCH 9/9] docs(influxdb3): Add product-specific TOCs for Core and Enterprise metrics - Split TOC into separate Core and Enterprise versions using show-in shortcodes - Core TOC focuses on single-node monitoring workflows - Enterprise TOC includes cluster-specific and node-specific monitoring sections - Improves navigation by showing only relevant sections per product - Fix: Remove duplicate "InfluxDB" word in metrics.md --- .../influxdb3/core/admin/monitor-metrics.md | 4 +- .../admin/last-value-cache/_index.md | 2 +- .../enterprise/admin/monitor-metrics.md | 4 +- .../admin/tokens/resource/create.md | 4 +- .../library/official/system-metrics.md | 2 +- .../shared/influxdb3-admin/monitor-metrics.md | 65 ++++++++++++------- content/shared/influxdb3-reference/metrics.md | 17 ++++- test/{ => influxdb3}/debug_metrics_test.py | 0 test/{ => influxdb3}/run-metrics-tests.sh | 0 test/{ => influxdb3}/show-metrics-sample.py | 0 10 files changed, 66 insertions(+), 32 deletions(-) rename test/{ => influxdb3}/debug_metrics_test.py (100%) rename test/{ => influxdb3}/run-metrics-tests.sh (100%) rename test/{ => influxdb3}/show-metrics-sample.py (100%) diff --git a/content/influxdb3/core/admin/monitor-metrics.md b/content/influxdb3/core/admin/monitor-metrics.md index 1bf3ee6c8a..1d6b3403ec 100644 --- a/content/influxdb3/core/admin/monitor-metrics.md +++ b/content/influxdb3/core/admin/monitor-metrics.md @@ -8,11 +8,13 @@ menu: influxdb3_core: parent: Administer InfluxDB name: Monitor metrics -weight: 105 +weight: 110 influxdb3/core/tags: [monitoring, metrics, prometheus, observability, operations] related: - /influxdb3/core/reference/internals/runtime-architecture/ - /influxdb3/core/admin/performance-tuning/ + - /influxdb3/core/plugins/library/, InfluxDB 3 Core plugins + - /influxdb3/core/write-data/use-telegraf/ - /influxdb3/core/reference/telemetry/ source: /shared/influxdb3-admin/monitor-metrics.md --- diff --git a/content/influxdb3/enterprise/admin/last-value-cache/_index.md b/content/influxdb3/enterprise/admin/last-value-cache/_index.md index 570432067c..17b509ac75 100644 --- a/content/influxdb3/enterprise/admin/last-value-cache/_index.md +++ b/content/influxdb3/enterprise/admin/last-value-cache/_index.md @@ -9,7 +9,7 @@ description: > menu: influxdb3_enterprise: parent: Administer InfluxDB -weight: 105 +weight: 106 influxdb3/enterprise/tags: [cache] related: - /influxdb3/enterprise/reference/sql/functions/cache/#last_cache, last_cache SQL function diff --git a/content/influxdb3/enterprise/admin/monitor-metrics.md b/content/influxdb3/enterprise/admin/monitor-metrics.md index 68375f0096..3e936d48c3 100644 --- a/content/influxdb3/enterprise/admin/monitor-metrics.md +++ b/content/influxdb3/enterprise/admin/monitor-metrics.md @@ -8,12 +8,14 @@ menu: influxdb3_enterprise: parent: Administer InfluxDB name: Monitor metrics -weight: 105 +weight: 110 influxdb3/enterprise/tags: [monitoring, metrics, prometheus, observability, operations, clustering] related: - /influxdb3/enterprise/admin/clustering/ - /influxdb3/enterprise/reference/internals/runtime-architecture/ - /influxdb3/enterprise/admin/performance-tuning/ + - /influxdb3/enterprise/plugins/library/, InfluxDB 3 Enterprise plugins + - /influxdb3/enterprise/write-data/use-telegraf/ - /influxdb3/enterprise/reference/telemetry/ source: /shared/influxdb3-admin/monitor-metrics.md --- diff --git a/content/influxdb3/enterprise/admin/tokens/resource/create.md b/content/influxdb3/enterprise/admin/tokens/resource/create.md index ce58142235..75214a2597 100644 --- a/content/influxdb3/enterprise/admin/tokens/resource/create.md +++ b/content/influxdb3/enterprise/admin/tokens/resource/create.md @@ -404,7 +404,7 @@ In your terminal, run the `influxdb3 create token --permission` command and prov The following example shows how to create specific system tokens: -{{% code-placeholders "(System [a-z]+ token|1y" %}} +{{% code-placeholders "(System [a-z]*\s?token)|1y" %}} ```bash influxdb3 create token \ --permission "system:health:read" \ @@ -458,7 +458,7 @@ In the request body, provide the following parameters: The following example shows how to use the HTTP API to create specific system tokens: -{{% code-placeholders "AUTH_TOKEN|(System [a-z]+ token)|300000" %}} +{{% code-placeholders "AUTH_TOKEN|(System [a-z]*\s?token)|1y|300000" %}} ```bash curl \ diff --git a/content/influxdb3/enterprise/plugins/library/official/system-metrics.md b/content/influxdb3/enterprise/plugins/library/official/system-metrics.md index a9c730b491..d0e3101084 100644 --- a/content/influxdb3/enterprise/plugins/library/official/system-metrics.md +++ b/content/influxdb3/enterprise/plugins/library/official/system-metrics.md @@ -12,4 +12,4 @@ related: source: /shared/influxdb3-plugins/plugins-library/official/system-metrics.md --- - \ No newline at end of file + diff --git a/content/shared/influxdb3-admin/monitor-metrics.md b/content/shared/influxdb3-admin/monitor-metrics.md index 8605571fc4..2a50e04b85 100644 --- a/content/shared/influxdb3-admin/monitor-metrics.md +++ b/content/shared/influxdb3-admin/monitor-metrics.md @@ -1,6 +1,25 @@ Use InfluxDB metrics to monitor {{% show-in "enterprise" %}}distributed cluster {{% /show-in %}}system performance, resource usage, and operational health with monitoring tools like Prometheus, Grafana, or other observability platforms. +{{% show-in "core" %}} +- [Access metrics](#access-metrics) +- [Metric categories](#metric-categories) +- [Key metrics for monitoring](#key-metrics-for-monitoring) +- [Example monitoring queries](#example-monitoring-queries) +- [Integration with monitoring tools](#integration-with-monitoring-tools) +- [Best practices](#best-practices) +{{% /show-in %}} + +{{% show-in "enterprise" %}} +- [Access metrics](#access-metrics) +- [Metric categories](#metric-categories) +- [Cluster-specific metrics](#cluster-specific-metrics) +- [Node-specific monitoring](#node-specific-monitoring) +- [Example monitoring queries](#example-monitoring-queries) +- [Distributed monitoring setup](#distributed-monitoring-setup) +- [Best practices](#best-practices) +{{% /show-in %}} + ## Access metrics An {{< product-name >}} node exposes metrics at the `/metrics` endpoint on the HTTP port (default: 8181). @@ -58,7 +77,8 @@ done ### Metrics format -Metrics are exposed in [Prometheus exposition format](https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format): +InfluxDB exposes metrics in [Prometheus exposition format](https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format), a format supported by many tools, including [Telegraf](#collect-metrics-with-telegraf). +Each metric follows this structure: ``` # HELP metric_name Description of the metric @@ -109,6 +129,9 @@ Monitor API request patterns{{% show-in "enterprise" %}} across the cluster{{% / - **`grpc_requests_total`**: Total gRPC requests{{% show-in "enterprise" %}} for inter-node communication{{% /show-in %}} - **`grpc_request_duration_seconds`**: gRPC request latency distribution +> [!Note] +> Monitor all write endpoints (`/api/v3/write_lp`, `/api/v2/write`, `/write`) and query endpoints (`/api/v3/query_sql`, `/api/v3/query_influxql`, `/query`) for comprehensive request tracking. + ### Database operations Monitor database{{% show-in "enterprise" %}}-specific and distributed cluster{{% /show-in %}} operations: @@ -122,14 +145,14 @@ Monitor database{{% show-in "enterprise" %}}-specific and distributed cluster{{% Different metrics are more relevant depending on node [mode configuration](/influxdb3/version/admin/clustering/#configure-node-modes): #### Ingest nodes (mode: ingest) -- **`http_requests_total{path="/api/v3/write"}`**: Write request volume +- **`http_requests_total{path=~"/api/v3/write_lp|/api/v2/write|/write"}`**: Write request volume (all endpoints) - **`object_store_transfer_bytes_total`**: WAL-to-Parquet snapshot activity - **`datafusion_mem_pool_bytes`**: Memory usage for snapshot operations #### Query nodes (mode: query) - **`influxdb_iox_query_log_*`**: Query execution performance - **`influxdb3_parquet_cache_*`**: Cache performance for query acceleration -- **`http_requests_total{path~"/api/v3/query.*"}`**: Query request patterns +- **`http_requests_total{path=~"/api/v3/query_sql|/api/v3/query_influxql|/query"}`**: Query request patterns (all endpoints) #### Compactor nodes (mode: compact) - **`object_store_op_duration_seconds`**: Compaction operation performance @@ -189,27 +212,15 @@ Monitor runtime health and resource usage: {{% show-in "enterprise" %}} ## Cluster-specific metrics -### Node coordination - -Monitor how nodes work together: - -```bash -# Check ingester response coordination -curl -s http://query-node:8181/metrics | grep 'influxdb_iox_query_log_ingester_latency' - -# Monitor catalog operation conflicts -curl -s http://any-node:8181/metrics | grep 'influxdb3_catalog_operation_retries_total' -``` - ### Load distribution Monitor workload distribution across nodes: ```bash -# Write load across ingest nodes +# Write load across ingest nodes (all write endpoints) for node in ingester-01 ingester-02; do echo "Node $node:" - curl -s http://$node:8181/metrics | grep 'http_requests_total.*v3/write.*status="ok"' + curl -s http://$node:8181/metrics | grep 'http_requests_total.*\(api/v3/write_lp\|api/v2/write\|/write\).*status="ok"' done # Query load across query nodes @@ -226,8 +237,8 @@ done Monitor data ingestion performance: ```bash -# Ingest throughput -curl -s http://ingester-01:8181/metrics | grep 'http_requests_total.*v3/write' +# Ingest throughput (all write endpoints) +curl -s http://ingester-01:8181/metrics | grep 'http_requests_total.*\(api/v3/write_lp\|api/v2/write\|/write\)' # Snapshot creation activity curl -s http://ingester-01:8181/metrics | grep 'object_store_transfer_bytes_total.*put' @@ -272,8 +283,8 @@ curl -s http://compactor-01:8181/metrics | grep 'object_store_transfer_objects_t Monitor data ingestion: ```bash -# HTTP requests to write endpoints -curl -s http://localhost:8181/metrics | grep 'http_requests_total.*v3/write\|http_requests_total.*v2/write' +# HTTP requests to write endpoints (all endpoints) +curl -s http://localhost:8181/metrics | grep 'http_requests_total.*\(api/v3/write_lp\|api/v2/write\|/write\)' # Object store writes (Parquet file creation) curl -s http://localhost:8181/metrics | grep 'object_store_transfer.*total.*put' @@ -338,8 +349,8 @@ Use these queries in Prometheus or Grafana dashboards: # Total requests per second across all nodes sum(rate(http_requests_total[5m])) by (instance) -# Write requests per second by ingest node -sum(rate(http_requests_total{path="/api/v3/write"}[5m])) by (instance) +# Write requests per second by ingest node (all write endpoints) +sum(rate(http_requests_total{path=~"/api/v3/write_lp|/api/v2/write|/write"}[5m])) by (instance) ``` #### Query performance across nodes @@ -779,6 +790,14 @@ groups: --> {{% /show-in %}} +### Extend monitoring with InfluxDB 3 plugins + +Use {{< product-name >}} plugins to extend monitoring and alerting capabilities: + +- [Notifier plugin](/influxdb3/version/plugins/library/official/notifier/): Send alerts to external systems based on custom logic. +- [Threshold deadman checks plugin](/influxdb3/version/plugins/library/official/threshold-deadman-checks/): Monitor metrics and trigger alerts when thresholds are breached. +- [System metrics plugin](/influxdb3/version/plugins/library/official/system-metrics/): Collect and visualize system-level metrics. + ## Best practices ### General monitoring practices diff --git a/content/shared/influxdb3-reference/metrics.md b/content/shared/influxdb3-reference/metrics.md index d9e8ab73b5..663d7d0ec5 100644 --- a/content/shared/influxdb3-reference/metrics.md +++ b/content/shared/influxdb3-reference/metrics.md @@ -49,9 +49,20 @@ curl -s http://compactor-01:8181/metrics {{% /show-in %}} ``` +# Write endpoints http_requests_total{method="POST",method_path="POST /api/v3/write_lp",path="/api/v3/write_lp",status="ok"} 1 +http_requests_total{method="POST",method_path="POST /api/v2/write",path="/api/v2/write",status="ok"} 1 +http_requests_total{method="POST",method_path="POST /write",path="/write",status="ok"} 1 + +# Query endpoints +http_requests_total{method="POST",method_path="POST /api/v3/query_sql",path="/api/v3/query_sql",status="ok"} 1 +http_requests_total{method="POST",method_path="POST /api/v3/query_influxql",path="/api/v3/query_influxql",status="ok"} 1 +http_requests_total{method="GET",method_path="GET /query",path="/query",status="ok"} 1 ``` +> [!Note] +> Monitor all write endpoints (`/api/v3/write_lp`, `/api/v2/write`, `/write`) and query endpoints (`/api/v3/query_sql`, `/api/v3/query_influxql`, `/query`) for comprehensive request tracking. + ### http_request_duration_seconds - **Type:** Histogram - **Description:** Distribution of HTTP request latencies{{% show-in "enterprise" %}} per node{{% /show-in %}} @@ -551,12 +562,12 @@ Focus on these metrics for cluster health: Monitor different metrics based on [node specialization](/influxdb3/enterprise/admin/clustering/): - **Ingest nodes or all-in-one nodes handling writes**: - - `http_requests_total{path="/api/v3/write_lp"}` - Write operations via HTTP + - `http_requests_total{path=~"/api/v3/write_lp|/api/v2/write|/write"}` - Write operations via HTTP (all endpoints) - `grpc_requests_total{path="/api/v3/write_lp"}` - Write operations via gRPC - `grpc_request_duration_seconds{path="/api/v3/write_lp"}` - Write operation latency - `object_store_transfer_bytes_total{op="put"}` - Data written to object storage - **Query nodes or all-in-one nodes handling queries**: - - `http_requests_total{path="/api/v3/query_sql"}` - SQL query requests + - `http_requests_total{path=~"/api/v3/query_sql|/api/v3/query_influxql|/query"}` - Query requests (all endpoints) - `influxdb_iox_query_log_execute_duration_seconds` - Query execution time - `influxdb3_parquet_cache_access_total` - Parquet cache performance - **All nodes (configuration and management)**: @@ -570,7 +581,7 @@ Monitor different metrics based on [node specialization](/influxdb3/enterprise/a ## Prometheus format -InfluxDB metrics are exposed in Prometheus exposition format, a text-based format that includes metric names, labels, and values. Each metric follows this structure: +InfluxDB exposes metrics in Prometheus exposition format, a text-based format that includes metric names, labels, and values. Each metric follows this structure: ``` metric_name{label1="value1",label2="value2"} metric_value timestamp diff --git a/test/debug_metrics_test.py b/test/influxdb3/debug_metrics_test.py similarity index 100% rename from test/debug_metrics_test.py rename to test/influxdb3/debug_metrics_test.py diff --git a/test/run-metrics-tests.sh b/test/influxdb3/run-metrics-tests.sh similarity index 100% rename from test/run-metrics-tests.sh rename to test/influxdb3/run-metrics-tests.sh diff --git a/test/show-metrics-sample.py b/test/influxdb3/show-metrics-sample.py similarity index 100% rename from test/show-metrics-sample.py rename to test/influxdb3/show-metrics-sample.py