Merge branch 'master' into copilot/fix-6297

pull/6298/head
Jason Stirnaman 2025-08-19 08:29:57 -05:00 committed by GitHub
commit 1a58595083
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 2235 additions and 1826 deletions

View File

@ -0,0 +1,66 @@
# Lychee link checker configuration
# Generated by link-checker
[lychee]
# Performance settings
# Maximum number of retries for failed checks
max_retries = 3
# Timeout for each link check (in seconds)
timeout = 30
# Maximum number of concurrent checks
max_concurrency = 128
skip_code_blocks = false
# HTTP settings
# Identify the tool to external services
user_agent = "Mozilla/5.0 (compatible; link-checker)"
# Accept these HTTP status codes as valid
accept = [200, 201, 202, 203, 204, 206, 301, 302, 303, 304,
307, 308]
# Skip these URL schemes
scheme = ["file", "mailto", "tel"]
# Exclude patterns (regex supported)
exclude = [
# Localhost URLs
"^https?://localhost",
"^https?://127\\.0\\.0\\.1",
# Common CI/CD environments
"^https?://.*\\.local",
# Example domains used in documentation
"^https?://example\\.(com|org|net)",
# Placeholder URLs from code block filtering
"https://example.com/REMOVED_FROM_CODE_BLOCK",
"example.com/INLINE_CODE_URL",
# URLs that require authentication
"^https?://.*\\.slack\\.com",
"^https?://.*\\.atlassian\\.net",
# GitHub URLs (often fail due to rate limiting and bot
# detection)
"^https?://github\\.com",
# Common documentation placeholders
"YOUR_.*",
"REPLACE_.*",
"<.*>",
]
# Request headers
[headers]
# Add custom headers here if needed
# "Authorization" = "Bearer $GITHUB_TOKEN"
# Cache settings
cache = true
max_cache_age = "1d"

View File

@ -0,0 +1,108 @@
# Production Link Checker Configuration for InfluxData docs-v2
# Optimized for performance, reliability, and reduced false positives
[lychee]
# Performance settings
# Maximum number of retries for failed checks
max_retries = 3
# Timeout for each link check (in seconds)
timeout = 30
# Maximum number of concurrent checks
max_concurrency = 128
skip_code_blocks = false
# HTTP settings
# Identify the tool to external services
"User-Agent" = "Mozilla/5.0 (compatible; influxdata-link-checker/1.0; +https://github.com/influxdata/docs-v2)"
accept = [200, 201, 202, 203, 204, 206, 301, 302, 303, 304, 307, 308]
# Skip these URL schemes
scheme = ["mailto", "tel"]
# Performance optimizations
cache = true
max_cache_age = "1h"
# Retry configuration for reliability
include_verbatim = false
# Exclusion patterns for docs-v2 (regex supported)
exclude = [
# Localhost URLs
"^https?://localhost",
"^https?://127\\.0\\.0\\.1",
# Common CI/CD environments
"^https?://.*\\.local",
# Example domains used in documentation
"^https?://example\\.(com|org|net)",
# Placeholder URLs from code block filtering
"https://example.com/REMOVED_FROM_CODE_BLOCK",
"example.com/INLINE_CODE_URL",
# URLs that require authentication
"^https?://.*\\.slack\\.com",
"^https?://.*\\.atlassian\\.net",
# GitHub URLs (often fail due to rate limiting and bot
# detection)
"^https?://github\\.com",
# Social media URLs (often block bots)
"^https?://reddit\\.com",
"^https?://.*\\.reddit\\.com",
# InfluxData support URLs (certificate/SSL issues in CI)
"^https?://support\\.influxdata\\.com",
# Common documentation placeholders
"YOUR_.*",
"REPLACE_.*",
"<.*>",
]
# Request headers
[headers]
# Add custom headers here if needed
# "Authorization" = "Bearer $GITHUB_TOKEN"
"Accept" = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
"Accept-Language" = "en-US,en;q=0.5"
"Accept-Encoding" = "gzip, deflate"
"DNT" = "1"
"Connection" = "keep-alive"
"Upgrade-Insecure-Requests" = "1"
[ci]
# CI-specific settings
[ci.github_actions]
output_format = "json"
create_annotations = true
fail_fast = false
max_annotations = 50 # Limit to avoid overwhelming PR comments
[ci.performance]
# Performance tuning for CI environment
parallel_requests = 32
connection_timeout = 10
read_timeout = 30
# Resource limits
max_memory_mb = 512
max_execution_time_minutes = 10
[reporting]
# Report configuration
include_fragments = false
verbose = false
no_progress = true # Disable progress bar in CI
# Summary settings
show_success_count = true
show_skipped_count = true

View File

@ -1,103 +0,0 @@
name: 'Report Broken Links'
description: 'Downloads broken link reports, generates PR comment, and posts results'
inputs:
github-token:
description: 'GitHub token for posting comments'
required: false
default: ${{ github.token }}
max-links-per-file:
description: 'Maximum links to show per file in comment'
required: false
default: '20'
include-success-message:
description: 'Include success message when no broken links found'
required: false
default: 'true'
outputs:
has-broken-links:
description: 'Whether broken links were found (true/false)'
value: ${{ steps.generate-comment.outputs.has-broken-links }}
broken-link-count:
description: 'Number of broken links found'
value: ${{ steps.generate-comment.outputs.broken-link-count }}
runs:
using: 'composite'
steps:
- name: Download broken link reports
uses: actions/download-artifact@v4
with:
path: reports
continue-on-error: true
- name: Generate PR comment
id: generate-comment
run: |
# Generate comment using our script
node .github/scripts/comment-generator.js \
--max-links ${{ inputs.max-links-per-file }} \
${{ inputs.include-success-message == 'false' && '--no-success' || '' }} \
--output-file comment.md \
reports/ || echo "No reports found or errors occurred"
# Check if comment file was created and has content
if [[ -f comment.md && -s comment.md ]]; then
echo "comment-generated=true" >> $GITHUB_OUTPUT
# Count broken links by parsing the comment
broken_count=$(grep -o "Found [0-9]* broken link" comment.md | grep -o "[0-9]*" || echo "0")
echo "broken-link-count=$broken_count" >> $GITHUB_OUTPUT
# Check if there are actually broken links (not just a success comment)
if [[ "$broken_count" -gt 0 ]]; then
echo "has-broken-links=true" >> $GITHUB_OUTPUT
else
echo "has-broken-links=false" >> $GITHUB_OUTPUT
fi
else
echo "has-broken-links=false" >> $GITHUB_OUTPUT
echo "broken-link-count=0" >> $GITHUB_OUTPUT
echo "comment-generated=false" >> $GITHUB_OUTPUT
fi
shell: bash
- name: Post PR comment
if: steps.generate-comment.outputs.comment-generated == 'true'
uses: actions/github-script@v7
with:
github-token: ${{ inputs.github-token }}
script: |
const fs = require('fs');
if (fs.existsSync('comment.md')) {
const comment = fs.readFileSync('comment.md', 'utf8');
if (comment.trim()) {
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
}
}
- name: Report validation results
run: |
has_broken_links="${{ steps.generate-comment.outputs.has-broken-links }}"
broken_count="${{ steps.generate-comment.outputs.broken-link-count }}"
if [ "$has_broken_links" = "true" ]; then
echo "::error::❌ Link validation failed: Found $broken_count broken link(s)"
echo "Check the PR comment for detailed broken link information"
exit 1
else
echo "::notice::✅ Link validation passed successfully"
echo "All links in the changed files are valid"
if [ "${{ steps.generate-comment.outputs.comment-generated }}" = "true" ]; then
echo "PR comment posted with validation summary and cache statistics"
fi
fi
shell: bash

View File

@ -1,106 +0,0 @@
name: 'Validate Links'
description: 'Runs e2e browser-based link validation tests against Hugo site using Cypress'
inputs:
files:
description: 'Space-separated list of files to validate'
required: true
product-name:
description: 'Product name for reporting (optional)'
required: false
default: ''
cache-enabled:
description: 'Enable link validation caching'
required: false
default: 'true'
cache-key:
description: 'Cache key prefix for this validation run'
required: false
default: 'link-validation'
timeout:
description: 'Test timeout in seconds'
required: false
default: '900'
outputs:
failed:
description: 'Whether validation failed (true/false)'
value: ${{ steps.validate.outputs.failed }}
runs:
using: 'composite'
steps:
- name: Restore link validation cache
if: inputs.cache-enabled == 'true'
uses: actions/cache@v4
with:
path: .cache/link-validation
key: ${{ inputs.cache-key }}-${{ runner.os }}-${{ hashFiles('content/**/*.md', 'content/**/*.html') }}
restore-keys: |
${{ inputs.cache-key }}-${{ runner.os }}-
${{ inputs.cache-key }}-
- name: Run link validation
shell: bash
run: |
# Set CI-specific environment variables
export CI=true
export GITHUB_ACTIONS=true
export NODE_OPTIONS="--max-old-space-size=4096"
# Set test runner timeout for Hugo shutdown
export HUGO_SHUTDOWN_TIMEOUT=5000
# Add timeout to prevent hanging (timeout command syntax: timeout DURATION COMMAND)
timeout ${{ inputs.timeout }}s node cypress/support/run-e2e-specs.js ${{ inputs.files }} \
--spec cypress/e2e/content/article-links.cy.js || {
exit_code=$?
# Handle timeout specifically
if [ $exit_code -eq 124 ]; then
echo "::error::Link validation timed out after ${{ inputs.timeout }} seconds"
echo "::notice::This may indicate Hugo server startup issues or very slow link validation"
else
echo "::error::Link validation failed with exit code $exit_code"
fi
# Check for specific error patterns and logs (but don't dump full content)
if [ -f /tmp/hugo_server.log ]; then
echo "Hugo server log available for debugging"
fi
if [ -f hugo.log ]; then
echo "Additional Hugo log available for debugging"
fi
if [ -f /tmp/broken_links_report.json ]; then
# Only show summary, not full report (full report is uploaded as artifact)
broken_count=$(grep -o '"url":' /tmp/broken_links_report.json | wc -l || echo "0")
echo "Broken links report contains $broken_count entries"
fi
exit $exit_code
}
# Report success if we get here
echo "::notice::✅ Link validation completed successfully"
echo "No broken links detected in the tested files"
- name: Upload logs on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: validation-logs-${{ inputs.product-name && inputs.product-name || 'default' }}
path: |
hugo.log
/tmp/hugo_server.log
if-no-files-found: ignore
- name: Upload broken links report
if: always()
uses: actions/upload-artifact@v4
with:
name: broken-links-report${{ inputs.product-name && format('-{0}', inputs.product-name) || '' }}
path: /tmp/broken_links_report.json
if-no-files-found: ignore

View File

@ -1,134 +1,285 @@
# Instructions for InfluxData Documentation
# InfluxData Documentation Repository (docs-v2)
## Purpose and scope
Always follow these instructions first and fallback to additional search and context gathering only when the information provided here is incomplete or found to be in error.
Help document InfluxData products by creating clear, accurate technical content with proper code examples, frontmatter, and formatting.
## Working Effectively
## Documentation structure
### Bootstrap, Build, and Test the Repository
Execute these commands in order to set up a complete working environment:
1. **Install Node.js dependencies** (takes ~4 seconds):
```bash
# Skip Cypress binary download due to network restrictions in CI environments
CYPRESS_INSTALL_BINARY=0 yarn install
```
2. **Build the static site** (takes ~75 seconds, NEVER CANCEL - set timeout to 180+ seconds):
```bash
npx hugo --quiet
```
3. **Start the development server** (builds in ~92 seconds, NEVER CANCEL - set timeout to 150+ seconds):
```bash
npx hugo server --bind 0.0.0.0 --port 1313
```
- Access at: http://localhost:1313/
- Serves 5,359+ pages and 441 static files
- Auto-rebuilds on file changes
4. **Alternative Docker development setup** (use if local Hugo fails):
```bash
docker compose up local-dev
```
**Note**: May fail in restricted network environments due to Alpine package manager issues.
### Testing (CRITICAL: NEVER CANCEL long-running tests)
#### Code Block Testing (takes 5-15 minutes per product, NEVER CANCEL - set timeout to 30+ minutes):
```bash
# Build test environment first (takes ~30 seconds, may fail due to network restrictions)
docker build -t influxdata/docs-pytest:latest -f Dockerfile.pytest .
# Test all products (takes 15-45 minutes total)
yarn test:codeblocks:all
# Test specific products
yarn test:codeblocks:cloud
yarn test:codeblocks:v2
yarn test:codeblocks:telegraf
```
#### Link Validation (takes 10-30 minutes, NEVER CANCEL - set timeout to 45+ minutes):
```bash
# Test all links (very long-running)
yarn test:links
# Test specific files/products (faster)
yarn test:links content/influxdb3/core/**/*.md
yarn test:links:v3
yarn test:links:v2
```
#### Style Linting (takes 30-60 seconds):
```bash
# Basic Vale linting
docker compose run -T vale content/**/*.md
# Product-specific linting with custom configurations
docker compose run -T vale --config=content/influxdb3/cloud-dedicated/.vale.ini --minAlertLevel=error content/influxdb3/cloud-dedicated/**/*.md
```
#### JavaScript and CSS Linting (takes 5-10 seconds):
```bash
yarn eslint assets/js/**/*.js
yarn prettier --check "**/*.{css,js,ts,jsx,tsx}"
```
### Pre-commit Hooks (automatically run, can be skipped if needed):
```bash
# Run all pre-commit checks manually
yarn lint
# Skip pre-commit hooks if necessary (not recommended)
git commit -m "message" --no-verify
```
## Validation Scenarios
Always test these scenarios after making changes to ensure full functionality:
### 1. Documentation Rendering Test
```bash
# Start Hugo server
npx hugo server --bind 0.0.0.0 --port 1313
# Verify key pages load correctly (200 status)
curl -s -o /dev/null -w "%{http_code}" http://localhost:1313/influxdb3/core/
curl -s -o /dev/null -w "%{http_code}" http://localhost:1313/influxdb/v2/
curl -s -o /dev/null -w "%{http_code}" http://localhost:1313/telegraf/v1/
# Verify content contains expected elements
curl -s http://localhost:1313/influxdb3/core/ | grep -i "influxdb"
```
### 2. Build Output Validation
```bash
# Verify build completes successfully
npx hugo --quiet
# Check build output exists and has reasonable size (~529MB)
ls -la public/
du -sh public/
# Verify key files exist
file public/index.html
file public/influxdb3/core/index.html
```
### 3. Shortcode and Formatting Test
```bash
# Test shortcode examples page
yarn test:links content/example.md
```
## Repository Structure and Key Locations
### Content Organization
- **InfluxDB 3**: `/content/influxdb3/` (core, enterprise, cloud-dedicated, cloud-serverless, clustered, explorer)
- **InfluxDB v2**: `/content/influxdb/` (v2, cloud, enterprise_influxdb, v1)
- **Telegraf**: `/content/telegraf/v1/`
- **Other tools**: `/content/kapacitor/`, `/content/chronograf/`, `/content/flux/`
- **Shared content**: `/content/shared/`
- **Examples**: `/content/example.md` (comprehensive shortcode reference)
### Configuration Files
- **Hugo config**: `/config/_default/`
- **Package management**: `package.json`, `yarn.lock`
- **Docker**: `compose.yaml`, `Dockerfile.pytest`
- **Git hooks**: `lefthook.yml`
- **Testing**: `cypress.config.js`, `pytest.ini` (in test directories)
- **Linting**: `.vale.ini`, `.prettierrc.yaml`, `eslint.config.js`
### Build and Development
- **Hugo binary**: Available via `npx hugo` (version 0.148.2+)
- **Static assets**: `/assets/` (JavaScript, CSS, images)
- **Build output**: `/public/` (generated, ~529MB)
- **Layouts**: `/layouts/` (Hugo templates)
- **Data files**: `/data/` (YAML/JSON data for templates)
## Technology Stack
- **Static Site Generator**: Hugo (0.148.2+ extended)
- **Package Manager**: Yarn (1.22.22+) with Node.js (20.19.4+)
- **Testing Framework**:
- Pytest with pytest-codeblocks (for code examples)
- Cypress (for link validation and E2E tests)
- Vale (for style and writing guidelines)
- **Containerization**: Docker with Docker Compose
- **Linting**: ESLint, Prettier, Vale
- **Git Hooks**: Lefthook
## Common Tasks and Build Times
### Time Expectations (CRITICAL - NEVER CANCEL)
- **Dependency installation**: 4 seconds
- **Hugo static build**: 75 seconds (NEVER CANCEL - timeout: 180+ seconds)
- **Hugo server startup**: 92 seconds (NEVER CANCEL - timeout: 150+ seconds)
- **Code block tests**: 5-15 minutes per product (NEVER CANCEL - timeout: 30+ minutes)
- **Link validation**: 10-30 minutes (NEVER CANCEL - timeout: 45+ minutes)
- **Style linting**: 30-60 seconds
- **Docker image build**: 30+ seconds (may fail due to network restrictions)
### Network Connectivity Issues
In restricted environments, these commands may fail due to external dependency downloads:
- `docker build -t influxdata/docs-pytest:latest -f Dockerfile.pytest .` (InfluxData repositories, HashiCorp repos)
- `docker compose up local-dev` (Alpine package manager)
- Cypress binary installation (use `CYPRESS_INSTALL_BINARY=0`)
Document these limitations but proceed with available functionality.
### Validation Commands for CI
Always run these before committing changes:
```bash
# Format and lint code
yarn prettier --write "**/*.{css,js,ts,jsx,tsx}"
yarn eslint assets/js/**/*.js
# Test Hugo build
npx hugo --quiet
# Test development server startup
timeout 150 npx hugo server --bind 0.0.0.0 --port 1313 &
sleep 120
curl -s -o /dev/null -w "%{http_code}" http://localhost:1313/
pkill hugo
```
## Key Projects in This Codebase
1. **InfluxDB 3 Documentation** (Core, Enterprise, Clustered, Cloud Dedicated, Cloud Serverless, and InfluxDB 3 plugins for Core and Enterprise)
2. **InfluxDB 3 Explorer** (UI)
3. **InfluxDB v2 Documentation** (OSS and Cloud)
3. **InfuxDB v1 Documentation** (OSS and Enterprise)
4. **Telegraf Documentation** (agent and plugins)
5. **Supporting Tools Documentation** (Kapacitor, Chronograf, Flux)
6. **API Reference Documentation** (`/api-docs/`)
7. **Shared Documentation Components** (`/content/shared/`)
## Important Locations for Frequent Tasks
- **Shortcode reference**: `/content/example.md`
- **Contributing guide**: `CONTRIBUTING.md`
- **Testing guide**: `TESTING.md`
- **Product configurations**: `/data/products.yml`
- **Vale style rules**: `/.ci/vale/styles/`
- **GitHub workflows**: `/.github/workflows/`
- **Test scripts**: `/test/scripts/`
- **Hugo layouts and shortcodes**: `/layouts/`
- **CSS/JS assets**: `/assets/`
## Content Guidelines and Style
### Documentation Structure
- **Product version data**: `/data/products.yml`
- **InfluxData products**:
- InfluxDB 3 Explorer
- Documentation source path: `/content/influxdb3/explorer`
- Published for the web: https://docs.influxdata.com/influxdb3/explorer/
- InfluxDB 3 Core
- Documentation source path: `/content/influxdb3/core`
- Published for the web: https://docs.influxdata.com/influxdb3/core/
- Code repositories: https://github.com/influxdata/influxdb, https://github.com/influxdata/influxdb3_core
- InfluxDB 3 Enterprise
- Documentation source path: `/content/influxdb3/enterprise`
- Published for the web: https://docs.influxdata.com/influxdb3/enterprise/
- Code repositories: https://github.com/influxdata/influxdb, https://github.com/influxdata/influxdb3_enterprise
- InfluxDB Cloud Dedicated
- Documentation source path: `/content/influxdb3/cloud-dedicated`
- Published for the web: https://docs.influxdata.com/influxdb3/cloud-dedicated/
- Code repository: https://github.com/influxdata/influxdb
- InfluxDB Cloud Serverless
- Documentation source path: `/content/influxdb3/cloud-serverless`
- Published for the web: https://docs.influxdata.com/influxdb3/cloud-serverless/
- Code repository: https://github.com/influxdata/idpe
- InfluxDB Cloud v2 (TSM)
- Documentation source path: `/content/influxdb/cloud`
- Published for the web: https://docs.influxdata.com/influxdb/cloud/
- Code repository: https://github.com/influxdata/idpe
- InfluxDB Clustered
- Documentation source path: `/content/influxdb3/clustered`
- Published for the web: https://docs.influxdata.com/influxdb3/clustered/
- Code repository: https://github.com/influxdata/influxdb
- InfluxDB Enterprise v1 (1.x)
- Documentation source path: `/content/influxdb/enterprise_influxdb`
- Published for the web: https://docs.influxdata.com/enterprise_influxdb/v1/
- Code repository: https://github.com/influxdata/influxdb
- InfluxDB OSS 1.x
- Documentation source path: `/content/influxdb/v1`
- Published for the web: https://docs.influxdata.com/influxdb/v1/
- Code repository: https://github.com/influxdata/influxdb
- InfluxDB OSS 2.x
- Documentation source path: `/content/influxdb/v2`
- Published for the web: https://docs.influxdata.com/influxdb/v2/
- Code repository: https://github.com/influxdata/influxdb
- Telegraf
- Documentation source path: `/content/telegraf/v1`
- Published for the web: https://docs.influxdata.com/telegraf/v1/
- Code repository: https://github.com/influxdata/telegraf
- Kapacitor
- Documentation source path: `/content/kapacitor/v1`
- Published for the web: https://docs.influxdata.com/kapacitor/v1/
- Code repository: https://github.com/influxdata/kapacitor
- Chronograf
- Documentation source path: `/content/chronograf/v1`
- Published for the web: https://docs.influxdata.com/chronograf/v1/
- Code repository: https://github.com/influxdata/chronograf
- Flux
- Documentation source path: `/content/flux/v0`
- Published for the web: https://docs.influxdata.com/flux/v0/
- Code repository: https://github.com/influxdata/flux
- **InfluxData-supported tools**:
- InfluxDB API client libraries
- Code repositories: https://github.com/InfluxCommunity
- InfluxDB 3 processing engine plugins
- Code repository: https://github.com/influxdata/influxdb3_plugins
- **Query Languages**: SQL, InfluxQL, Flux (use appropriate language per product version)
- **Documentation Site**: https://docs.influxdata.com
- **Repository**: https://github.com/influxdata/docs-v2
- **Framework**: Hugo static site generator
## Abbreviations and shortcuts
- `gdd`: Google Developer Documentation style
- `3core`: InfluxDB 3 Core
- `3ent`: InfluxDB 3 Enterprise
## Style guidelines
### Style Guidelines
- Follow Google Developer Documentation style guidelines
- For API references, follow YouTube Data API style
- Use semantic line feeds (one sentence per line)
- Format code examples to fit within 80 characters
- Command line examples:
- Should be formatted as code blocks
- Should use long options (e.g., `--option` instead of `-o`)
- Use cURL for API examples
- Format to fit within 80 characters
- Should use `--data-urlencode` for query parameters
- Should use `--header` for headers
- Use only h2-h6 headings in content (h1 comes from frontmatter title properties)
- Use sentence case for headings
- Use GitHub callout syntax
- 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`
- Use appropriate product names and versions consistently
- Follow InfluxData vocabulary guidelines
## Markdown and shortcodes
### Markdown and Shortcodes
- Include proper frontmatter for Markdown pages in `content/**/*.md` (except for
shared content files in `content/shared/`):
Include proper frontmatter for all content pages:
```yaml
title: # Page title (h1)
seotitle: # SEO title
list_title: # Title for article lists
description: # SEO description
menu:
product_version:
weight: # Page order (1-99, 101-199, etc.)
```
- Follow the shortcode examples in `content/example.md` and the documentation
for docs-v2 contributors in `CONTRIBUTING.md`
- Use provided shortcodes correctly:
- Notes/warnings: `{{% note %}}`, `{{% warn %}}`
- Product-specific: `{{% enterprise %}}`, `{{% cloud %}}`
- Tabbed content: `{{< tabs-wrapper >}}`, `{{% tabs %}}`, `{{% tab-content %}}`
- Tabbed content for code examples (without additional text): `{{< code-tabs-wrapper >}}`, `{{% code-tabs %}}`, `{{% code-tab-content %}}`
- Version links: `{{< latest >}}`, `{{< latest-patch >}}`
- API endpoints: `{{< api-endpoint >}}`
- Required elements: `{{< req >}}`
- Navigation: `{{< page-nav >}}`
- Diagrams: `{{< diagram >}}`, `{{< filesystem-diagram >}}`
```yaml
title: # Page title (h1)
seotitle: # SEO title
description: # SEO description
menu:
product_version:
weight: # Page order (1-99, 101-199, etc.)
```
## Code examples and testing
Key shortcodes (see `/content/example.md` for full reference):
- Provide complete, working examples with proper testing annotations:
- Notes/warnings (GitHub syntax): `> [!Note]`, `> [!Warning]`
- Tabbed content: `{{< tabs-wrapper >}}`, `{{% tabs %}}`, `{{% tab-content %}}`
- Code examples: `{{< code-tabs-wrapper >}}`, `{{% code-tabs %}}`, `{{% code-tab-content %}}`
- Required elements: `{{< req >}}`
- API endpoints: `{{< api-endpoint >}}`
### Code Examples and Testing
Provide complete, working examples with pytest annotations:
```python
print("Hello, world!")
@ -140,67 +291,21 @@ print("Hello, world!")
Hello, world!
```
- CLI command example:
## Troubleshooting Common Issues
```sh
influx query 'from(bucket:"example") |> range(start:-1h)'
```
1. **"Pytest collected 0 items"**: Use `python` (not `py`) for code block language identifiers
2. **Hugo build errors**: Check `/config/_default/` for configuration issues
3. **Docker build failures**: Expected in restricted networks - document and continue with local Hugo
4. **Cypress installation failures**: Use `CYPRESS_INSTALL_BINARY=0 yarn install`
5. **Link validation slow**: Use file-specific testing: `yarn test:links content/specific-file.md`
6. **Vale linting errors**: Check `.ci/vale/styles/config/vocabularies` for accepted/rejected terms
<!--pytest-codeblocks:expected-output-->
```
Table: keys: [_start, _stop, _field, _measurement]
_start:time _stop:time _field:string _measurement:string _time:time _value:float
------------------------------ ------------------------------ ---------------------- ---------------------- ------------------------------ ----------------------------
```
- Include necessary environment variables
- Show proper credential handling for authenticated commands
## API documentation
- `/api-docs` contains OpenAPI spec files used for API reference documentation
- Follow OpenAPI specification patterns
- Match REST API examples to current implementation
- Include complete request/response examples
- Document required headers and authentication
## Versioning and product differentiation
- Clearly distinguish between different InfluxDB versions (1.x, 2.x, 3.x)
- Use correct terminology for each product variant
- Apply appropriate UI descriptions and screenshots
- Reference appropriate query language per version
## Development tools
- Vale.sh linter for style checking
- Configuration file: `.vale.ini`
- Docker for local development and testing
- pytest and pytest-codeblocks for validating code examples
- Use cypress for testing documentation UI and links
- Prettier for code formatting
- ESLint for JavaScript and TypeScript linting
- Lefthook (NPM package) for managing pre-commit hooks for quality assurance
## Code style
- Use modern JavaScript (ES6+) syntax
## Related repositories
- **Internal documentation assistance requests**: https://github.com/influxdata/DAR/issues Documentation
## Additional instruction files
## Additional Instruction Files
For specific workflows and content types, also refer to:
- **InfluxDB 3 code placeholders**: `.github/instructions/influxdb3-code-placeholders.instructions.md` - Guidelines for placeholder formatting, descriptions, and shortcode usage in InfluxDB 3 documentation
- **Contributing guidelines**: `.github/instructions/contributing.instructions.md` - Detailed style guidelines, shortcode usage, frontmatter requirements, and development workflows
- **Content-specific instructions**: Check `.github/instructions/` directory for specialized guidelines covering specific documentation patterns and requirements
- **InfluxDB 3 code placeholders**: `.github/instructions/influxdb3-code-placeholders.instructions.md`
- **Contributing guidelines**: `.github/instructions/contributing.instructions.md`
- **Content-specific instructions**: Check `.github/instructions/` directory
## Integration with specialized instructions
When working on InfluxDB 3 documentation (Core/Enterprise), prioritize the placeholder guidelines from `influxdb3-code-placeholders.instructions.md`.
For general documentation structure, shortcodes, and development workflows, follow the comprehensive guidelines in `contributing.instructions.md`.
Remember: This is a large documentation site with complex build processes. Patience with build times is essential, and NEVER CANCEL long-running operations.

241
.github/workflows/pr-link-check.yml vendored Normal file
View File

@ -0,0 +1,241 @@
name: Link Check PR Changes
on:
pull_request:
paths:
- 'content/**/*.md'
- 'data/**/*.yml'
- 'layouts/**/*.html'
types: [opened, synchronize, reopened]
jobs:
link-check:
name: Check links in affected files
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect content changes
id: detect
run: |
echo "🔍 Detecting changes between ${{ github.base_ref }} and ${{ github.sha }}"
# For PRs, use the GitHub Files API to get changed files
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
echo "Using GitHub API to detect PR changes..."
curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }}/files" \
| jq -r '.[].filename' > all_changed_files.txt
else
echo "Using git diff to detect changes..."
git diff --name-only ${{ github.event.before }}..${{ github.sha }} > all_changed_files.txt
fi
# Filter for content markdown files
CHANGED_FILES=$(grep '^content/.*\.md$' all_changed_files.txt || true)
echo "📁 All changed files:"
cat all_changed_files.txt
echo ""
echo "📝 Content markdown files:"
echo "$CHANGED_FILES"
if [[ -n "$CHANGED_FILES" ]]; then
echo "✅ Found $(echo "$CHANGED_FILES" | wc -l) changed content file(s)"
echo "has-changes=true" >> $GITHUB_OUTPUT
echo "changed-content<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_FILES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Check if any shared content files were modified
SHARED_CHANGES=$(echo "$CHANGED_FILES" | grep '^content/shared/' || true)
if [[ -n "$SHARED_CHANGES" ]]; then
echo "has-shared-content=true" >> $GITHUB_OUTPUT
echo "🔄 Detected shared content changes: $SHARED_CHANGES"
else
echo "has-shared-content=false" >> $GITHUB_OUTPUT
fi
else
echo "❌ No content changes detected"
echo "has-changes=false" >> $GITHUB_OUTPUT
echo "has-shared-content=false" >> $GITHUB_OUTPUT
fi
- name: Skip if no content changes
if: steps.detect.outputs.has-changes == 'false'
run: |
echo "No content changes detected in this PR - skipping link check"
echo "✅ **No content changes detected** - link check skipped" >> $GITHUB_STEP_SUMMARY
- name: Setup Node.js
if: steps.detect.outputs.has-changes == 'true'
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'
- name: Install dependencies
if: steps.detect.outputs.has-changes == 'true'
run: yarn install --frozen-lockfile
- name: Build Hugo site
if: steps.detect.outputs.has-changes == 'true'
run: npx hugo --minify
- name: Download link-checker binary
if: steps.detect.outputs.has-changes == 'true'
run: |
echo "Downloading link-checker binary from docs-v2 releases..."
# Download from docs-v2's own releases (always accessible)
curl -L -H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-o link-checker-info.json \
"https://api.github.com/repos/influxdata/docs-v2/releases/tags/link-checker-v1.2.2"
# Extract download URL for linux binary
DOWNLOAD_URL=$(jq -r '.assets[] | select(.name | test("link-checker.*linux")) | .url' link-checker-info.json)
if [[ "$DOWNLOAD_URL" == "null" || -z "$DOWNLOAD_URL" ]]; then
echo "❌ No linux binary found in release"
echo "Available assets:"
jq -r '.assets[].name' link-checker-info.json
exit 1
fi
echo "📥 Downloading: $DOWNLOAD_URL"
curl -L -H "Accept: application/octet-stream" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-o link-checker "$DOWNLOAD_URL"
chmod +x link-checker
./link-checker --version
- name: Verify link checker config exists
if: steps.detect.outputs.has-changes == 'true'
run: |
if [[ ! -f .ci/link-checker/production.lycherc.toml ]]; then
echo "❌ Configuration file .ci/link-checker/production.lycherc.toml not found"
echo "Please copy production.lycherc.toml from docs-tooling/link-checker/"
exit 1
fi
echo "✅ Using configuration: .ci/link-checker/production.lycherc.toml"
- name: Map changed content to public files
if: steps.detect.outputs.has-changes == 'true'
id: mapping
run: |
echo "Mapping changed content files to public HTML files..."
# Create temporary file with changed content files
echo "${{ steps.detect.outputs.changed-content }}" > changed-files.txt
# Map content files to public files
PUBLIC_FILES=$(cat changed-files.txt | xargs -r ./link-checker map --existing-only)
if [[ -n "$PUBLIC_FILES" ]]; then
echo "Found affected public files:"
echo "$PUBLIC_FILES"
echo "public-files<<EOF" >> $GITHUB_OUTPUT
echo "$PUBLIC_FILES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Count files for summary
FILE_COUNT=$(echo "$PUBLIC_FILES" | wc -l)
echo "file-count=$FILE_COUNT" >> $GITHUB_OUTPUT
else
echo "No public files found to check"
echo "public-files=" >> $GITHUB_OUTPUT
echo "file-count=0" >> $GITHUB_OUTPUT
fi
- name: Run link checker
if: steps.detect.outputs.has-changes == 'true' && steps.mapping.outputs.public-files != ''
id: link-check
run: |
echo "Checking links in ${{ steps.mapping.outputs.file-count }} affected files..."
# Create temporary file with public files list
echo "${{ steps.mapping.outputs.public-files }}" > public-files.txt
# Run link checker with detailed JSON output
set +e # Don't fail immediately on error
cat public-files.txt | xargs -r ./link-checker check \
--config .ci/link-checker/production.lycherc.toml \
--format json \
--output link-check-results.json
EXIT_CODE=$?
if [[ -f link-check-results.json ]]; then
# Parse results
BROKEN_COUNT=$(jq -r '.summary.broken_count // 0' link-check-results.json)
TOTAL_COUNT=$(jq -r '.summary.total_checked // 0' link-check-results.json)
SUCCESS_RATE=$(jq -r '.summary.success_rate // 0' link-check-results.json)
echo "broken-count=$BROKEN_COUNT" >> $GITHUB_OUTPUT
echo "total-count=$TOTAL_COUNT" >> $GITHUB_OUTPUT
echo "success-rate=$SUCCESS_RATE" >> $GITHUB_OUTPUT
if [[ $BROKEN_COUNT -gt 0 ]]; then
echo "❌ Found $BROKEN_COUNT broken links out of $TOTAL_COUNT total links"
echo "check-result=failed" >> $GITHUB_OUTPUT
else
echo "✅ All $TOTAL_COUNT links are valid"
echo "check-result=passed" >> $GITHUB_OUTPUT
fi
else
echo "❌ Link check failed to generate results"
echo "check-result=error" >> $GITHUB_OUTPUT
fi
exit $EXIT_CODE
- name: Process and report results
if: always() && steps.detect.outputs.has-changes == 'true' && steps.mapping.outputs.public-files != ''
run: |
if [[ -f link-check-results.json ]]; then
# Create detailed error annotations for broken links
if [[ "${{ steps.link-check.outputs.check-result }}" == "failed" ]]; then
echo "Creating error annotations for broken links..."
jq -r '.broken_links[]? |
"::error file=\(.file // "unknown"),line=\(.line // 1)::Broken link: \(.url) - \(.error // "Unknown error")"' \
link-check-results.json || true
fi
# Generate summary comment
cat >> $GITHUB_STEP_SUMMARY << 'EOF'
## Link Check Results
**Files Checked:** ${{ steps.mapping.outputs.file-count }}
**Total Links:** ${{ steps.link-check.outputs.total-count }}
**Broken Links:** ${{ steps.link-check.outputs.broken-count }}
**Success Rate:** ${{ steps.link-check.outputs.success-rate }}%
EOF
if [[ "${{ steps.link-check.outputs.check-result }}" == "failed" ]]; then
echo "❌ **Link check failed** - see annotations above for details" >> $GITHUB_STEP_SUMMARY
else
echo "✅ **All links are valid**" >> $GITHUB_STEP_SUMMARY
fi
else
echo "⚠️ **Link check could not complete** - no results file generated" >> $GITHUB_STEP_SUMMARY
fi
- name: Upload detailed results
if: always() && steps.detect.outputs.has-changes == 'true' && steps.mapping.outputs.public-files != ''
uses: actions/upload-artifact@v4
with:
name: link-check-results
path: |
link-check-results.json
changed-files.txt
public-files.txt
retention-days: 30

View File

@ -1,148 +0,0 @@
# PR Link Validation Workflow
# Provides basic and parallel workflows
# with smart strategy selection based on change volume
name: PR Link Validation
on:
pull_request:
paths:
- 'content/**/*.md'
- 'content/**/*.html'
- 'api-docs/**/*.yml'
- 'assets/**/*.js'
- 'layouts/**/*.html'
jobs:
# TEMPORARILY DISABLED - Remove this condition to re-enable link validation
disabled-check:
if: false # Set to true to re-enable the workflow
runs-on: ubuntu-latest
steps:
- run: echo "Link validation is temporarily disabled"
setup:
name: Setup and Strategy Detection
runs-on: ubuntu-latest
if: false # TEMPORARILY DISABLED - Remove this condition to re-enable
outputs:
strategy: ${{ steps.determine-strategy.outputs.strategy }}
has-changes: ${{ steps.determine-strategy.outputs.has-changes }}
matrix: ${{ steps.determine-strategy.outputs.matrix }}
all-files: ${{ steps.changed-files.outputs.all_changed_files }}
cache-hit-rate: ${{ steps.determine-strategy.outputs.cache-hit-rate }}
cache-hits: ${{ steps.determine-strategy.outputs.cache-hits }}
cache-misses: ${{ steps.determine-strategy.outputs.cache-misses }}
original-file-count: ${{ steps.determine-strategy.outputs.original-file-count }}
validation-file-count: ${{ steps.determine-strategy.outputs.validation-file-count }}
cache-message: ${{ steps.determine-strategy.outputs.message }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup docs environment
uses: ./.github/actions/setup-docs-env
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v41
with:
files: |
content/**/*.md
content/**/*.html
api-docs/**/*.yml
- name: Determine validation strategy
id: determine-strategy
run: |
if [[ "${{ steps.changed-files.outputs.any_changed }}" != "true" ]]; then
echo "No relevant files changed"
echo "strategy=none" >> $GITHUB_OUTPUT
echo "has-changes=false" >> $GITHUB_OUTPUT
echo "matrix={\"include\":[]}" >> $GITHUB_OUTPUT
echo "cache-hit-rate=100" >> $GITHUB_OUTPUT
echo "cache-hits=0" >> $GITHUB_OUTPUT
echo "cache-misses=0" >> $GITHUB_OUTPUT
exit 0
fi
# Use our matrix generator with cache awareness
files="${{ steps.changed-files.outputs.all_changed_files }}"
echo "🔍 Analyzing ${files} for cache-aware validation..."
# Generate matrix and capture outputs
result=$(node .github/scripts/matrix-generator.js \
--min-files-parallel 10 \
--max-concurrent 5 \
--output-format github \
$files)
# Parse all outputs from matrix generator
while IFS='=' read -r key value; do
case "$key" in
strategy|has-changes|cache-hit-rate|cache-hits|cache-misses|original-file-count|validation-file-count|message)
echo "$key=$value" >> $GITHUB_OUTPUT
;;
matrix)
echo "matrix=$value" >> $GITHUB_OUTPUT
;;
esac
done <<< "$result"
# Extract values for logging
strategy=$(echo "$result" | grep "^strategy=" | cut -d'=' -f2)
cache_hit_rate=$(echo "$result" | grep "^cache-hit-rate=" | cut -d'=' -f2)
cache_message=$(echo "$result" | grep "^message=" | cut -d'=' -f2-)
echo "📊 Selected strategy: $strategy"
if [[ -n "$cache_hit_rate" ]]; then
echo "📈 Cache hit rate: ${cache_hit_rate}%"
fi
if [[ -n "$cache_message" ]]; then
echo "$cache_message"
fi
validate:
name: ${{ matrix.name }}
needs: setup
if: false # TEMPORARILY DISABLED - Original condition: needs.setup.outputs.has-changes == 'true'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.matrix) }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup docs environment
uses: ./.github/actions/setup-docs-env
- name: Validate links
uses: ./.github/actions/validate-links
with:
files: ${{ matrix.files || needs.setup.outputs.all-files }}
product-name: ${{ matrix.product }}
cache-enabled: ${{ matrix.cacheEnabled || 'true' }}
cache-key: link-validation-${{ hashFiles(matrix.files || needs.setup.outputs.all-files) }}
timeout: 900
report:
name: Report Results
needs: [setup, validate]
if: false # TEMPORARILY DISABLED - Original condition: always() && needs.setup.outputs.has-changes == 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup docs environment
uses: ./.github/actions/setup-docs-env
- name: Report broken links
uses: ./.github/actions/report-broken-links
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
max-links-per-file: 20

View File

@ -0,0 +1,68 @@
name: Sync Link Checker Binary from docs-tooling
on:
workflow_dispatch:
inputs:
version:
description: 'Link checker version to sync (e.g., v1.2.2)'
required: true
type: string
jobs:
sync-binary:
name: Sync link-checker binary from docs-tooling
runs-on: ubuntu-latest
steps:
- name: Download binary from docs-tooling release
run: |
echo "Downloading link-checker ${{ inputs.version }} from docs-tooling..."
# Download binary from docs-tooling release
curl -L -H "Accept: application/octet-stream" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-o link-checker-linux-x86_64 \
"https://github.com/influxdata/docs-tooling/releases/download/link-checker-${{ inputs.version }}/link-checker-linux-x86_64"
# Download checksums
curl -L -H "Accept: application/octet-stream" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-o checksums.txt \
"https://github.com/influxdata/docs-tooling/releases/download/link-checker-${{ inputs.version }}/checksums.txt"
# Verify downloads
ls -la link-checker-linux-x86_64 checksums.txt
- name: Create docs-v2 release
run: |
echo "Creating link-checker-${{ inputs.version }} release in docs-v2..."
gh release create \
--title "Link Checker Binary ${{ inputs.version }}" \
--notes "Link validation tooling binary for docs-v2 GitHub Actions workflows.
This binary is distributed from the docs-tooling repository release link-checker-${{ inputs.version }}.
### Usage in GitHub Actions
The binary is automatically downloaded by docs-v2 workflows for link validation.
### Manual Usage
\`\`\`bash
# Download and make executable
curl -L -o link-checker https://github.com/influxdata/docs-v2/releases/download/link-checker-${{ inputs.version }}/link-checker-linux-x86_64
chmod +x link-checker
# Verify installation
./link-checker --version
\`\`\`
### Changes in ${{ inputs.version }}
See the [docs-tooling release](https://github.com/influxdata/docs-tooling/releases/tag/link-checker-${{ inputs.version }}) for detailed changelog." \
link-checker-${{ inputs.version }} \
link-checker-linux-x86_64 \
checksums.txt
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

13
.gitignore vendored
View File

@ -3,11 +3,14 @@
public
.*.swp
node_modules
package-lock.json
.config*
**/.env*
*.log
/resources
.hugo_build.lock
# Content generation
/content/influxdb*/**/api/**/*.html
!api-docs/**/.config.yml
/api-docs/redoc-static.html*
@ -16,18 +19,22 @@ node_modules
!telegraf-build/templates
!telegraf-build/scripts
!telegraf-build/README.md
# CI/CD tool files
/cypress/downloads/*
/cypress/screenshots/*
/cypress/videos/*
.lycheecache
test-results.xml
/influxdb3cli-build-scripts/content
tmp
# IDE files
.vscode/*
!.vscode/launch.json
.idea
**/config.toml
package-lock.json
tmp
# Context files for LLMs and AI tools
# User context files for AI assistant tools
.context/*
!.context/README.md

View File

@ -121,96 +121,251 @@ Potential causes:
# This is ignored
```
## Link Validation Testing
## Link Validation with Link-Checker
Link validation uses Cypress for e2e browser-based testing against the Hugo site to ensure all internal and external links work correctly.
Link validation uses the `link-checker` tool to validate internal and external links in documentation files.
### Basic Usage
#### Installation
**Option 1: Build from source (macOS/local development)**
For local development on macOS, build the link-checker from source:
```bash
# Test specific files
yarn test:links content/influxdb3/core/**/*.md
# Clone and build link-checker
git clone https://github.com/influxdata/docs-tooling.git
cd docs-tooling/link-checker
cargo build --release
# Test all links (may take a long time)
yarn test:links
# Test by product (may take a long time)
yarn test:links:v3
yarn test:links:v2
yarn test:links:telegraf
yarn test:links:chronograf
yarn test:links:kapacitor
# Copy binary to your PATH or use directly
cp target/release/link-checker /usr/local/bin/
# OR use directly: ./target/release/link-checker
```
### How Link Validation Works
**Option 2: Download pre-built binary (GitHub Actions/Linux)**
The tests:
1. Start a Hugo development server
2. Navigate to each page in a browser
3. Check all links for validity
4. Report broken or invalid links
The link-checker binary is distributed via docs-v2 releases for reliable access from GitHub Actions workflows:
```bash
# Download Linux binary from docs-v2 releases
curl -L -o link-checker \
https://github.com/influxdata/docs-v2/releases/download/link-checker-v1.0.0/link-checker-linux-x86_64
chmod +x link-checker
# Verify installation
./link-checker --version
```
> [!Note]
> Pre-built binaries are currently Linux x86_64 only. For macOS development, use Option 1 to build from source.
```bash
# Clone and build link-checker
git clone https://github.com/influxdata/docs-tooling.git
cd docs-tooling/link-checker
cargo build --release
# Copy binary to your PATH or use directly
cp target/release/link-checker /usr/local/bin/
```
#### Binary Release Process
**For maintainers:** To create a new link-checker release in docs-v2:
1. **Create release in docs-tooling** (builds and releases binary automatically):
```bash
cd docs-tooling
git tag link-checker-v1.2.x
git push origin link-checker-v1.2.x
```
2. **Manually distribute to docs-v2** (required due to private repository access):
```bash
# Download binary from docs-tooling release
curl -L -H "Authorization: Bearer $(gh auth token)" \
-o link-checker-linux-x86_64 \
"https://github.com/influxdata/docs-tooling/releases/download/link-checker-v1.2.x/link-checker-linux-x86_64"
curl -L -H "Authorization: Bearer $(gh auth token)" \
-o checksums.txt \
"https://github.com/influxdata/docs-tooling/releases/download/link-checker-v1.2.x/checksums.txt"
# Create docs-v2 release
gh release create \
--repo influxdata/docs-v2 \
--title "Link Checker Binary v1.2.x" \
--notes "Link validation tooling binary for docs-v2 GitHub Actions workflows." \
link-checker-v1.2.x \
link-checker-linux-x86_64 \
checksums.txt
```
3. **Update workflow reference** (if needed):
```bash
# Update .github/workflows/pr-link-check.yml line 98 to use new version
sed -i 's/link-checker-v[0-9.]*/link-checker-v1.2.x/' .github/workflows/pr-link-check.yml
```
> [!Note]
> The manual distribution is required because docs-tooling is a private repository and the default GitHub token doesn't have cross-repository access for private repos.
#### Core Commands
```bash
# Map content files to public HTML files
link-checker map content/path/to/file.md
# Check links in HTML files
link-checker check public/path/to/file.html
# Generate configuration file
link-checker config
```
### Link Resolution Behavior
The link-checker automatically handles relative link resolution based on the input type:
**Local Files → Local Resolution**
```bash
# When checking local files, relative links resolve to the local filesystem
link-checker check public/influxdb3/core/admin/scale-cluster/index.html
# Relative link /influxdb3/clustered/tags/kubernetes/ becomes:
# → /path/to/public/influxdb3/clustered/tags/kubernetes/index.html
```
**URLs → Production Resolution**
```bash
# When checking URLs, relative links resolve to the production site
link-checker check https://docs.influxdata.com/influxdb3/core/admin/scale-cluster/
# Relative link /influxdb3/clustered/tags/kubernetes/ becomes:
# → https://docs.influxdata.com/influxdb3/clustered/tags/kubernetes/
```
**Why This Matters**
- **Testing new content**: Tag pages generated locally will be found when testing local files
- **Production validation**: Production URLs validate against the live site
- **No false positives**: New content won't appear broken when testing locally before deployment
### Content Mapping Workflows
#### Scenario 1: Map and check InfluxDB 3 Core content
```bash
# Map Markdown files to HTML
link-checker map content/influxdb3/core/get-started/
# Check links in mapped HTML files
link-checker check public/influxdb3/core/get-started/
```
#### Scenario 2: Map and check shared CLI content
```bash
# Map shared content files
link-checker map content/shared/influxdb3-cli/
# Check the mapped output files
# (link-checker map outputs the HTML file paths)
link-checker map content/shared/influxdb3-cli/ | \
xargs link-checker check
```
#### Scenario 3: Direct HTML checking
```bash
# Check HTML files directly without mapping
link-checker check public/influxdb3/core/get-started/
```
#### Combined workflow for changed files
```bash
# Check only files changed in the last commit
git diff --name-only HEAD~1 HEAD | grep '\.md$' | \
xargs link-checker map | \
xargs link-checker check
```
### Configuration Options
#### Local usage (default configuration)
```bash
# Uses default settings or test.lycherc.toml if present
link-checker check public/influxdb3/core/get-started/
```
#### Production usage (GitHub Actions)
```bash
# Use production configuration with comprehensive exclusions
link-checker check \
--config .ci/link-checker/production.lycherc.toml \
public/influxdb3/core/get-started/
```
### GitHub Actions Integration
#### Composite Action
**Automated Integration (docs-v2)**
The `.github/actions/validate-links/` composite action provides reusable link validation:
The docs-v2 repository includes automated link checking for pull requests:
- **Trigger**: Runs automatically on PRs that modify content files
- **Binary distribution**: Downloads latest pre-built binary from docs-v2 releases
- **Smart detection**: Only checks files affected by PR changes
- **Production config**: Uses optimized settings with exclusions for GitHub, social media, etc.
- **Results reporting**: Broken links reported as GitHub annotations with detailed summaries
The workflow automatically:
1. Detects content changes in PRs using GitHub Files API
2. Downloads latest link-checker binary from docs-v2 releases
3. Builds Hugo site and maps changed content to public HTML files
4. Runs link checking with production configuration
5. Reports results with annotations and step summaries
**Manual Integration (other repositories)**
For other repositories, you can integrate link checking manually:
```yaml
- uses: ./.github/actions/validate-links
with:
files: "content/influxdb3/core/file.md content/influxdb/v2/file2.md"
product-name: "core"
cache-enabled: "true"
cache-key: "link-validation"
name: Link Check
on:
pull_request:
paths:
- 'content/**/*.md'
jobs:
link-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download link-checker
run: |
curl -L -o link-checker \
https://github.com/influxdata/docs-tooling/releases/latest/download/link-checker-linux-x86_64
chmod +x link-checker
cp target/release/link-checker ../../link-checker
cd ../..
- name: Build Hugo site
run: |
npm install
npx hugo --minify
- name: Check changed files
run: |
git diff --name-only origin/main HEAD | \
grep '\.md$' | \
xargs ./link-checker map | \
xargs ./link-checker check \
--config .ci/link-checker/production.lycherc.toml
```
#### Matrix Generator
The `.github/scripts/matrix-generator.js` script provides intelligent strategy selection:
- **Sequential validation**: For small changes (< 10 files) or single-product changes
- **Parallel validation**: For large changes across multiple products (up to 5 concurrent jobs)
Test locally:
```bash
node .github/scripts/matrix-generator.js content/influxdb3/core/file1.md content/influxdb/v2/file2.md
```
Configuration options:
- `--max-concurrent <n>`: Maximum parallel jobs (default: 5)
- `--force-sequential`: Force sequential execution
- `--min-files-parallel <n>`: Minimum files for parallel (default: 10)
### Caching for Link Validation
Link validation supports caching to improve performance:
- **Cache location**: `.cache/link-validation/` (local), GitHub Actions cache (CI)
- **Cache keys**: Based on content file hashes
- **TTL**: 30 days by default, configurable
#### Cache Configuration Options
```bash
# Use 7-day cache for more frequent validation
yarn test:links --cache-ttl=7 content/influxdb3/**/*.md
# Use 1-day cache via environment variable
LINK_CACHE_TTL_DAYS=1 yarn test:links content/**/*.md
# Clean up expired cache entries
node .github/scripts/incremental-validator.js --cleanup
```
#### How Caching Works
- **Cache key**: Based on file path + content hash (file changes invalidate cache immediately)
- **External links**: Cached for the TTL period since URLs rarely change
- **Internal links**: Effectively cached until file content changes
- **Automatic cleanup**: Expired entries are removed on access and via `--cleanup`
## Style Linting (Vale)
Style linting uses [Vale](https://vale.sh/) to enforce documentation writing standards, branding guidelines, and vocabulary consistency.

View File

@ -66,7 +66,22 @@ paths:
schema:
type: string
required: true
description: Bucket to write to. If none exists, InfluxDB creates a bucket with a default 3-day retention policy.
description: |
The database to write to.
**Database targeting:** In Cloud Dedicated, databases can be named using the `database_name/retention_policy_name` convention for InfluxQL compatibility. Cloud Dedicated does not use DBRP mappings. The db and rp parameters are used to construct the target database name following this naming convention.
**Auto-creation behavior:** Cloud Dedicated requires databases to be created before writing data. The v1 `/write` API does not automatically create databases. If the specified
database does not exist, the write request will fail.
Authentication: Requires a valid API token with _write_ permissions for the target database.
### Related
- [Write data to InfluxDB Cloud Dedicated](/influxdb3/cloud-dedicated/write-data/)
- [Manage databases in InfluxDB Cloud Dedicated](/influxdb3/cloud-dedicated/admin/databases/)
- [InfluxQL DBRP naming convention](/influxdb3/cloud-dedicated/admin/databases/create/#influxql-dbrp-naming-convention)
- [InfluxQL data retention policy mapping differences](/influxdb3/cloud-serverless/guides/prototype-evaluation/#influxql-data-retention-policy-mapping-differences)
- in: query
name: rp
schema:
@ -137,6 +152,160 @@ paths:
schema:
$ref: '#/components/schemas/Error'
/query:
get:
operationId: GetQueryV1
tags:
- Query
summary: Query using the InfluxDB v1 HTTP API
parameters:
- $ref: '#/components/parameters/TraceSpan'
- $ref: '#/components/parameters/AuthUserV1'
- $ref: '#/components/parameters/AuthPassV1'
- in: header
name: Accept
schema:
type: string
description: Specifies how query results should be encoded in the response. **Note:** With `application/csv`, query results include epoch timestamps instead of RFC3339 timestamps.
default: application/json
enum:
- application/json
- application/csv
- text/csv
- application/x-msgpack
- in: header
name: Accept-Encoding
description: The Accept-Encoding request HTTP header advertises which content encoding, usually a compression algorithm, the client is able to understand.
schema:
type: string
description: Specifies that the query response in the body should be encoded with gzip or not encoded with identity.
default: identity
enum:
- gzip
- identity
- in: query
name: chunked
description: |
If true, the response is divided into chunks of size `chunk_size`.
schema:
type: boolean
default: false
- in: query
name: chunk_size
description: |
The number of records that will go into a chunk.
This parameter is only used if `chunked=true`.
schema:
type: integer
default: 10000
- in: query
name: db
schema:
type: string
required: true
description: The database to query from.
- in: query
name: pretty
description: |
If true, the JSON response is formatted in a human-readable format.
schema:
type: boolean
default: false
- in: query
name: q
description: Defines the InfluxQL query to run.
required: true
schema:
type: string
- in: query
name: rp
schema:
type: string
description: |
The retention policy name for InfluxQL compatibility
Optional parameter that, when combined with the db parameter, forms the complete database name to query. In InfluxDB Cloud Dedicated, databases can be named using the
database_name/retention_policy_name convention for InfluxQL compatibility.
When a request specifies both `db` and `rp`, Cloud Dedicated combines them as `db/rp` to target the database--for example:
- If `db=mydb` and `rp=autogen`, the query targets the database named `mydb/autogen`
- If only `db=mydb` is provided (no `rp`), the query targets the database named `mydb`
Unlike InfluxDB v1 and Cloud Serverless, Cloud Dedicated does not use DBRP mappings or separate retention policy objects. This parameter exists solely for v1 API
compatibility and database naming conventions.
_Note: The retention policy name does not control data retention in Cloud Dedicated. Data retention is determined by the database's **retention period** setting._
### Related
- [InfluxQL DBRP naming convention](/influxdb3/cloud-dedicated/admin/databases/create/#influxql-dbrp-naming-convention)
- [InfluxQL data retention policy mapping differences](/influxdb3/cloud-serverless/guides/prototype-evaluation/#influxql-data-retention-policy-mapping-differences)
- name: epoch
description: |
Formats timestamps as unix (epoch) timestamps with the specified precision
instead of RFC3339 timestamps with nanosecond precision.
in: query
schema:
type: string
enum:
- h
- m
- s
- ms
- u
- µ
- ns
responses:
'200':
description: Query results
headers:
Content-Encoding:
description: The Content-Encoding entity header is used to compress the media-type. When present, its value indicates which encodings were applied to the entity-body
schema:
type: string
description: Specifies that the response in the body is encoded with gzip or not encoded with identity.
default: identity
enum:
- gzip
- identity
Trace-Id:
description: The Trace-Id header reports the request's trace ID, if one was generated.
schema:
type: string
description: Specifies the request's trace ID.
content:
application/csv:
schema:
$ref: '#/components/schemas/InfluxQLCSVResponse'
text/csv:
schema:
$ref: '#/components/schemas/InfluxQLCSVResponse'
application/json:
schema:
$ref: '#/components/schemas/InfluxQLResponse'
examples:
influxql-chunk_size_2:
value: |
{"results":[{"statement_id":0,"series":[{"name":"mymeas","columns":["time","myfield","mytag"],"values":[["2016-05-19T18:37:55Z",90,"1"],["2016-05-19T18:37:56Z",90,"1"]],"partial":true}],"partial":true}]}
{"results":[{"statement_id":0,"series":[{"name":"mymeas","columns":["time","myfield","mytag"],"values":[["2016-05-19T18:37:57Z",90,"1"],["2016-05-19T18:37:58Z",90,"1"]]}]}]}
application/x-msgpack:
schema:
type: string
format: binary
'429':
description: Token is temporarily over quota. The Retry-After header describes when to try the read again.
headers:
Retry-After:
description: A non-negative decimal integer indicating the seconds to delay after the response is received.
schema:
type: integer
format: int32
default:
description: Error processing query
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
post:
operationId: PostQueryV1
tags:
@ -148,6 +317,83 @@ paths:
text/plain:
schema:
type: string
application/json:
schema:
type: object
properties:
db:
type: string
description: |
The database name for InfluxQL queries.
Required parameter that specifies the database to query.
In InfluxDB Cloud Dedicated, this can be either:
- A simple database name (for example, `mydb`)
- The database portion of a `database_name/retention_policy_name` naming convention (used together with the `rp` parameter)
When used alone, `db` specifies the complete database name to query. When used with the `rp` parameter, they combine to form the full database name as `db/rp`--for example, if `db=mydb` and `rp=autogen`, the query targets the database named `mydb/autogen`.
Unlike InfluxDB Cloud Serverless, Cloud Dedicated does not use DBRP mappings. The database name directly corresponds to an existing database in your Cloud Dedicated cluster.
Examples:
- `db=mydb` - queries the database named `mydb`
- `db=mydb` with `rp=autogen` - queries the database named `mydb/autogen`
_Note: The specified database must exist in your Cloud Dedicated cluster. Queries will fail if the database does not exist._
### Related
- [InfluxQL DBRP naming convention](/influxdb3/cloud-dedicated/admin/databases/create/#influxql-dbrp-naming-convention)
- [Migrate data from InfluxDB 1.x to Cloud Dedicated](/influxdb3/cloud-dedicated/guides/migrate-data/migrate-1x-to-cloud-dedicated/)
- [InfluxQL data retention policy mapping differences between InfluxDB Cloud Dedicated and Cloud Serverless](/influxdb3/cloud-serverless/guides/prototype-evaluation/#influxql-data-retention-policy-mapping-differences)
rp:
description: |
The retention policy name for InfluxQL compatibility
Optional parameter that, when combined with the db parameter, forms the complete database name to query. In InfluxDB Cloud Dedicated, databases can be named using the
database_name/retention_policy_name convention for InfluxQL compatibility.
When a request specifies both `db` and `rp`, Cloud Dedicated combines them as `db/rp` to target the database--for example:
- If `db=mydb` and `rp=autogen`, the query targets the database named `mydb/autogen`
- If only `db=mydb` is provided (no `rp`), the query targets the database named `mydb`
Unlike InfluxDB v1 and Cloud Serverless, Cloud Dedicated does not use DBRP mappings or separate retention policy objects. This parameter exists solely for v1 API
compatibility and database naming conventions.
_Note: The retention policy name does not control data retention in Cloud Dedicated. Data retention is determined by the database's **retention period** setting._
### Related
- [InfluxQL DBRP naming convention](/influxdb3/cloud-dedicated/admin/databases/create/#influxql-dbrp-naming-convention)
- [Migrate data from InfluxDB 1.x to Cloud Dedicated](/influxdb3/cloud-dedicated/guides/migrate-data/migrate-1x-to-cloud-dedicated/)
- [InfluxQL data retention policy mapping differences](/influxdb3/cloud-serverless/guides/prototype-evaluation/#influxql-data-retention-policy-mapping-differences)
type: string
q:
description: Defines the InfluxQL query to run.
type: string
chunked:
description: |
If true, the response is divided into chunks of size `chunk_size`.
type: boolean
chunk_size:
description: |
The number of records that will go into a chunk.
This parameter is only used if `chunked=true`.
type: integer
default: 10000
epoch:
description: |
A unix timestamp precision.
type: string
enum:
- h
- m
- s
- ms
- u
- µ
- ns
parameters:
- $ref: '#/components/parameters/TraceSpan'
- $ref: '#/components/parameters/AuthUserV1'
@ -184,7 +430,7 @@ paths:
schema:
type: string
required: true
description: Bucket to query.
description: Database to query.
- in: query
name: rp
schema:

View File

@ -65,7 +65,7 @@ paths:
schema:
type: string
required: true
description: Bucket to write to. If none exists, InfluxDB creates a bucket with a default 3-day retention policy.
description: Database to write to. If none exists, InfluxDB creates a database with a default 3-day retention policy.
- in: query
name: rp
schema:
@ -136,6 +136,188 @@ paths:
schema:
$ref: '#/components/schemas/Error'
/query:
get:
operationId: GetQueryV1
tags:
- Query
summary: Query using the InfluxDB v1 HTTP API
parameters:
- $ref: '#/components/parameters/TraceSpan'
- $ref: '#/components/parameters/AuthUserV1'
- $ref: '#/components/parameters/AuthPassV1'
- in: header
name: Accept
schema:
type: string
description: Specifies how query results should be encoded in the response. **Note:** With `application/csv`, query results include epoch timestamps instead of RFC3339 timestamps.
default: application/json
enum:
- application/json
- application/csv
- text/csv
- application/x-msgpack
- in: header
name: Accept-Encoding
description: The Accept-Encoding request HTTP header advertises which content encoding, usually a compression algorithm, the client is able to understand.
schema:
type: string
description: Specifies that the query response in the body should be encoded with gzip or not encoded with identity.
default: identity
enum:
- gzip
- identity
- in: query
name: chunked
description: |
If true, the response is divided into chunks of size `chunk_size`.
schema:
type: boolean
default: false
- in: query
name: chunk_size
description: |
The number of records that will go into a chunk.
This parameter is only used if `chunked=true`.
schema:
type: integer
default: 10000
- in: query
name: db
schema:
type: string
required: true
description: |
The database name for InfluxQL queries
Required parameter that specifies the database to query via DBRP (Database Retention Policy) mapping. In Cloud Serverless, this parameter is used together with DBRP
mappings to identify which bucket to query.
The `db` parameter (optionally combined with `rp`) must have an existing DBRP mapping that points to a bucket. Without a valid DBRP mapping, queries will fail with an
authorization error.
**DBRP mapping requirements:**
- A DBRP mapping must exist before querying
- Mappings can be created automatically when writing data with the v1 API (if your token has permissions)
- Mappings can be created manually using the InfluxDB CLI or API
### Examples
- `db=mydb` - uses the default DBRP mapping for `mydb`
- `db=mydb` with `rp=weekly` - uses the DBRP mapping for `mydb/weekly`
_Note: Unlike the v1 `/write` endpoint which can auto-create buckets and mappings, the `/query` endpoint requires pre-existing DBRP mappings. The actual data is stored in and
queried from the bucket that the DBRP mapping points to._
### Related
- [Use the InfluxDB v1 query API and InfluxQL in Cloud Serverless](/influxdb3/cloud-serverless/query-data/execute-queries/v1-http/)
- [Map v1 databases and retention policies to buckets in Cloud Serverless](/influxdb3/cloud-serverless/guides/api-compatibility/v1/#map-v1-databases-and-retention-policies-to-buckets)
- [Migrate from InfluxDB 1.x to Cloud Serverless](/influxdb3/cloud-serverless/guides/migrate-data/migrate-1x-to-serverless/)
- in: query
name: pretty
description: |
If true, the JSON response is formatted in a human-readable format.
schema:
type: boolean
default: false
- in: query
name: q
description: Defines the InfluxQL query to run.
required: true
schema:
type: string
- in: query
name: rp
schema:
type: string
description: |
The retention policy name for InfluxQL queries
Optional parameter that specifies the retention policy to use when querying data with InfluxQL. In Cloud Serverless, this parameter works with DBRP (Database Retention
Policy) mappings to identify the target bucket.
When provided together with the `db` parameter, Cloud Serverless uses the DBRP mapping to determine which bucket to query. The combination of `db` and `rp` must have an
existing DBRP mapping that points to a bucket. If no `rp` is specified, Cloud Serverless uses the default retention policy mapping for the database.
Requirements: A DBRP mapping must exist for the db/rp combination before you can query data. DBRP mappings can be created:
- Automatically when writing data with the v1 API (if your token has sufficient permissions)
- Manually using the InfluxDB CLI or API
Example: If `db=mydb` and `rp=weekly`, the query uses the DBRP mapping for `mydb/weekly` to determine which bucket to query.
_Note: The retention policy name is used only for DBRP mapping. Actual data retention is controlled by the target bucket's retention period setting, not by the retention
policy name._
### Related
- [Use the InfluxDB v1 query API and InfluxQL in Cloud Serverless](/influxdb3/cloud-serverless/query-data/execute-queries/v1-http/)
- [Map v1 databases and retention policies to buckets in Cloud Serverless](/influxdb3/cloud-serverless/guides/api-compatibility/v1/#map-v1-databases-and-retention-policies-to-buckets)
- [Migrate from InfluxDB 1.x to Cloud Serverless](/influxdb3/cloud-serverless/guides/migrate-data/migrate-1x-to-serverless/)
- name: epoch
description: |
Formats timestamps as unix (epoch) timestamps with the specified precision
instead of RFC3339 timestamps with nanosecond precision.
in: query
schema:
type: string
enum:
- h
- m
- s
- ms
- u
- µ
- ns
responses:
'200':
description: Query results
headers:
Content-Encoding:
description: The Content-Encoding entity header is used to compress the media-type. When present, its value indicates which encodings were applied to the entity-body
schema:
type: string
description: Specifies that the response in the body is encoded with gzip or not encoded with identity.
default: identity
enum:
- gzip
- identity
Trace-Id:
description: The Trace-Id header reports the request's trace ID, if one was generated.
schema:
type: string
description: Specifies the request's trace ID.
content:
application/csv:
schema:
$ref: '#/components/schemas/InfluxQLCSVResponse'
text/csv:
schema:
$ref: '#/components/schemas/InfluxQLCSVResponse'
application/json:
schema:
$ref: '#/components/schemas/InfluxQLResponse'
examples:
influxql-chunk_size_2:
value: |
{"results":[{"statement_id":0,"series":[{"name":"mymeas","columns":["time","myfield","mytag"],"values":[["2016-05-19T18:37:55Z",90,"1"],["2016-05-19T18:37:56Z",90,"1"]],"partial":true}],"partial":true}]}
{"results":[{"statement_id":0,"series":[{"name":"mymeas","columns":["time","myfield","mytag"],"values":[["2016-05-19T18:37:57Z",90,"1"],["2016-05-19T18:37:58Z",90,"1"]]}]}]}
application/x-msgpack:
schema:
type: string
format: binary
'429':
description: Token is temporarily over quota. The Retry-After header describes when to try the read again.
headers:
Retry-After:
description: A non-negative decimal integer indicating the seconds to delay after the response is received.
schema:
type: integer
format: int32
default:
description: Error processing query
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
post:
operationId: PostQueryV1
tags:
@ -147,6 +329,87 @@ paths:
text/plain:
schema:
type: string
application/json:
schema:
type: object
properties:
db:
type: string
description: |
The database name for InfluxQL queries
Required parameter that specifies the database to query via DBRP (Database Retention Policy) mapping. In Cloud Serverless, this parameter is used together with DBRP
mappings to identify which bucket to query.
The `db` parameter (optionally combined with `rp`) must have an existing DBRP mapping that points to a bucket. Without a valid DBRP mapping, queries will fail with an
authorization error.
**DBRP mapping requirements:**
- A DBRP mapping must exist before querying
- Mappings can be created automatically when writing data with the v1 API (if your token has permissions)
- Mappings can be created manually using the InfluxDB CLI or API
### Examples
- `db=mydb` - uses the default DBRP mapping for `mydb`
- `db=mydb` with `rp=weekly` - uses the DBRP mapping for `mydb/weekly`
_Note: Unlike the v1 `/write` endpoint which can auto-create buckets and mappings, the `/query` endpoint requires pre-existing DBRP mappings. The actual data is stored in and
queried from the bucket that the DBRP mapping points to._
### Related
- [Execute InfluxQL queries using the v1 API](/influxdb3/cloud-serverless/query-data/execute-queries/influxql/api/v1-http/)
- [Map v1 databases and retention policies to buckets in Cloud Serverless](/influxdb3/cloud-serverless/guides/api-compatibility/v1/#map-v1-databases-and-retention-policies-to-buckets)
- [Manage DBRP mappings in Cloud Serverless](/influxdb3/cloud-serverless/admin/dbrp/)
rp:
description: |
The retention policy name for InfluxQL queries
Optional parameter that specifies the retention policy to use when querying data with InfluxQL. In Cloud Serverless, this parameter works with DBRP (Database Retention
Policy) mappings to identify the target bucket.
When provided together with the `db` parameter, Cloud Serverless uses the DBRP mapping to determine which bucket to query. The combination of `db` and `rp` must have an
existing DBRP mapping that points to a bucket. If no `rp` is specified, Cloud Serverless uses the default retention policy mapping for the database.
Requirements: A DBRP mapping must exist for the db/rp combination before you can query data. DBRP mappings can be created:
- Automatically when writing data with the v1 API (if your token has sufficient permissions)
- Manually using the InfluxDB CLI or API
Example: If `db=mydb` and `rp=weekly`, the query uses the DBRP mapping for `mydb/weekly` to determine which bucket to query.
_Note: The retention policy name is used only for DBRP mapping. Actual data retention is controlled by the target bucket's retention period setting, not by the retention policy name._
### Related
- [Execute InfluxQL queries using the v1 API](/influxdb3/cloud-serverless/query-data/execute-queries/influxql/api/v1-http/)
- [Map v1 databases and retention policies to buckets in Cloud Serverless](/influxdb3/cloud-serverless/guides/api-compatibility/v1/#map-v1-databases-and-retention-policies-to-buckets)
- [Manage DBRP mappings in Cloud Serverless](/influxdb3/cloud-serverless/admin/dbrp/)
type: string
q:
description: Defines the InfluxQL query to run.
type: string
chunked:
description: |
If true, the response is divided into chunks of size `chunk_size`.
type: boolean
chunk_size:
description: |
The number of records that will go into a chunk.
This parameter is only used if `chunked=true`.
type: integer
default: 10000
epoch:
description: |
A unix timestamp precision.
type: string
enum:
- h
- m
- s
- ms
- u
- µ
- ns
parameters:
- $ref: '#/components/parameters/TraceSpan'
- $ref: '#/components/parameters/AuthUserV1'

View File

@ -65,7 +65,23 @@ paths:
schema:
type: string
required: true
description: Bucket to write to. If none exists, InfluxDB creates a bucket with a default 3-day retention policy.
description: |
The database to write to.
**Database targeting:** In InfluxDB Clustered, databases can be named using the `database_name/retention_policy_name` convention for InfluxQL compatibility. InfluxDB Clustered does not use DBRP mappings. The db and rp parameters are used to construct the target database name following this naming convention.
**Auto-creation behavior:** InfluxDB Clustered requires databases to be created before writing data. The v1 `/write` API does not automatically create databases. If the specified
database does not exist, the write request will fail.
Authentication: Requires a valid API token with _write_ permissions for the target database.
### Related
- [Write data to InfluxDB Clustered](/influxdb3/clustered/write-data/)
- [Use the InfluxDB v1 API with InfluxDB Clustered](/influxdb3/clustered/guides/api-compatibility/v1/)
- [Manage databases in InfluxDB Clustered](/influxdb3/clustered/admin/databases/)
- [InfluxQL DBRP naming convention in InfluxDB Clustered](/influxdb3/clustered/admin/databases/create/#influxql-dbrp-naming-convention)
- [Migrate data from InfluxDB v1 to InfluxDB Clustered](/influxdb3/clustered/guides/migrate-data/migrate-1x-to-clustered/)
- in: query
name: rp
schema:
@ -136,6 +152,141 @@ paths:
schema:
$ref: '#/components/schemas/Error'
/query:
get:
operationId: GetQueryV1
tags:
- Query
summary: Query using the InfluxDB v1 HTTP API
parameters:
- $ref: '#/components/parameters/TraceSpan'
- $ref: '#/components/parameters/AuthUserV1'
- $ref: '#/components/parameters/AuthPassV1'
- in: header
name: Accept
schema:
type: string
description: Specifies how query results should be encoded in the response. **Note:** With `application/csv`, query results include epoch timestamps instead of RFC3339 timestamps.
default: application/json
enum:
- application/json
- application/csv
- text/csv
- application/x-msgpack
- in: header
name: Accept-Encoding
description: The Accept-Encoding request HTTP header advertises which content encoding, usually a compression algorithm, the client is able to understand.
schema:
type: string
description: Specifies that the query response in the body should be encoded with gzip or not encoded with identity.
default: identity
enum:
- gzip
- identity
- in: query
name: chunked
description: |
If true, the response is divided into chunks of size `chunk_size`.
schema:
type: boolean
default: false
- in: query
name: chunk_size
description: |
The number of records that will go into a chunk.
This parameter is only used if `chunked=true`.
schema:
type: integer
default: 10000
- in: query
name: db
schema:
type: string
required: true
description: The database to query from.
- in: query
name: pretty
description: |
If true, the JSON response is formatted in a human-readable format.
schema:
type: boolean
default: false
- in: query
name: q
description: Defines the InfluxQL query to run.
required: true
schema:
type: string
- in: query
name: rp
schema:
type: string
description: Retention policy name.
- name: epoch
description: |
Formats timestamps as unix (epoch) timestamps with the specified precision
instead of RFC3339 timestamps with nanosecond precision.
in: query
schema:
type: string
enum:
- h
- m
- s
- ms
- u
- µ
- ns
responses:
'200':
description: Query results
headers:
Content-Encoding:
description: The Content-Encoding entity header is used to compress the media-type. When present, its value indicates which encodings were applied to the entity-body
schema:
type: string
description: Specifies that the response in the body is encoded with gzip or not encoded with identity.
default: identity
enum:
- gzip
- identity
Trace-Id:
description: The Trace-Id header reports the request's trace ID, if one was generated.
schema:
type: string
description: Specifies the request's trace ID.
content:
application/csv:
schema:
$ref: '#/components/schemas/InfluxQLCSVResponse'
text/csv:
schema:
$ref: '#/components/schemas/InfluxQLCSVResponse'
application/json:
schema:
$ref: '#/components/schemas/InfluxQLResponse'
examples:
influxql-chunk_size_2:
value: |
{"results":[{"statement_id":0,"series":[{"name":"mymeas","columns":["time","myfield","mytag"],"values":[["2016-05-19T18:37:55Z",90,"1"],["2016-05-19T18:37:56Z",90,"1"]],"partial":true}],"partial":true}]}
{"results":[{"statement_id":0,"series":[{"name":"mymeas","columns":["time","myfield","mytag"],"values":[["2016-05-19T18:37:57Z",90,"1"],["2016-05-19T18:37:58Z",90,"1"]]}]}]}
application/x-msgpack:
schema:
type: string
format: binary
'429':
description: Token is temporarily over quota. The Retry-After header describes when to try the read again.
headers:
Retry-After:
description: A non-negative decimal integer indicating the seconds to delay after the response is received.
schema:
type: integer
format: int32
default:
description: Error processing query
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
post:
operationId: PostQueryV1
tags:
@ -147,6 +298,64 @@ paths:
text/plain:
schema:
type: string
application/json:
schema:
type: object
properties:
db:
type: string
description: Database to query.
rp:
description: |
The retention policy name for InfluxQL compatibility
Optional parameter that, when combined with the db parameter, forms the complete database name to query. In InfluxDB Clustered, databases can be named using the
database_name/retention_policy_name convention for InfluxQL compatibility.
When a request specifies both `db` and `rp`, InfluxDB Clustered combines them as `db/rp` to target the database--for example:
- If `db=mydb` and `rp=autogen`, the query targets the database named `mydb/autogen`
- If only `db=mydb` is provided (no `rp`), the query targets the database named `mydb`
Unlike InfluxDB v1 and Cloud Serverless, InfluxDB Clustered does not use DBRP mappings or separate retention policy objects. This parameter exists solely for v1 API
compatibility and database naming conventions.
Note: The retention policy name does not control data retention in InfluxDB Clustered. Data retention is determined by the database's _retention period_ setting.
### Related
- [Use the v1 query API and InfluxQL to query data in InfluxDB Clustered](/influxdb3/clustered/query-data/execute-queries/influxdb-v1-api/)
- [Use the InfluxDB v1 API with InfluxDB Clustered](/influxdb3/clustered/guides/api-compatibility/v1/)
- [Manage databases in InfluxDB Clustered](/influxdb3/clustered/admin/databases/)
- [InfluxQL DBRP naming convention in InfluxDB Clustered](/influxdb3/clustered/admin/databases/create/#influxql-dbrp-naming-convention)
- [Migrate data from InfluxDB v1 to InfluxDB Clustered](/influxdb3/clustered/guides/migrate-data/migrate-1x-to-clustered/)
type: string
q:
description: |
Defines the InfluxQL query to run.
type: string
chunked:
description: |
If true, the response is divided into chunks of size `chunk_size`.
type: boolean
chunk_size:
description: |
The number of records that will go into a chunk.
This parameter is only used if `chunked=true`.
type: integer
default: 10000
epoch:
description: |
A unix timestamp precision.
type: string
enum:
- h
- m
- s
- ms
- u
- µ
- ns
parameters:
- $ref: '#/components/parameters/TraceSpan'
- $ref: '#/components/parameters/AuthUserV1'

View File

@ -10,6 +10,12 @@ aliases:
- /chronograf/v1/about_the_project/release-notes-changelog/
---
## v1.10.8 {date="2025-08-15"}
### Bug Fixes
- Fix missing retention policies on the Databases page.
## v1.10.7 {date="2025-04-15"}
### Bug Fixes

View File

@ -1,5 +1,5 @@
---
title: InfluxDB Enterprise 1.11 release notes
title: InfluxDB Enterprise v1 release notes
description: >
Important changes and what's new in each version InfluxDB Enterprise.
menu:
@ -7,9 +7,16 @@ menu:
name: Release notes
weight: 10
parent: About the project
alt_links:
v1: /influxdb/v1/about_the_project/release-notes/
---
## v1.12.1 {date="2025-06-26"}
## v1.12.x {date="TBD"}
> [!Important]
> #### Pre-release documentation
>
> This release is not yet available. [**v{{% latest-patch %}}**](#v1118) is the latest InfluxDB Enterprise v1 release.
> [!Important]
> #### Upgrade meta nodes first
@ -22,31 +29,53 @@ menu:
- Add additional log output when using
[`influx_inspect buildtsi`](/enterprise_influxdb/v1/tools/influx_inspect/#buildtsi) to
rebuild the TSI index.
<!-- TODO: Uncomment with 1.12.x release:
- Use [`influx_inspect export`](/enterprise_influxdb/v1/tools/influx_inspect/#export) with
[`-tsmfile` option](/enterprise_influxdb/v1/tools/influx_inspect/#--tsmfile-tsm_file-) to
export a single TSM file.
-->
<!-- TODO: Remove with 1.12.x release: -->
- Use [`influx_inspect export`](/enterprise_influxdb/v1/tools/influx_inspect/#export) with
`-tsmfile` option to
export a single TSM file.
- Add `-m` flag to the [`influxd-ctl show-shards` command](/enterprise_influxdb/v1/tools/influxd-ctl/show-shards/)
to output inconsistent shards.
- Allow the specification of a write window for retention policies.
- Add `fluxQueryRespBytes` metric to the `/debug/vars` metrics endpoint.
- Log whenever meta gossip times exceed expiration.
<!-- TODO: Uncomment with 1.12.x release:
- Add [`query-log-path` configuration option](/enterprise_influxdb/v1/administration/configure/config-data-nodes/#query-log-path)
to data nodes.
- Add [`aggressive-points-per-block` configuration option](/influxdb/v1/administration/config/#aggressive-points-per-block)
to prevent TSM files from not getting fully compacted.
-->
<!-- TODO: Remove with 1.12.x release: -->
- Add `query-log-path` configuration option to data nodes.
- Add `aggressive-points-per-block` configuration option to prevent TSM files from not getting fully compacted.
- Log TLS configuration settings on startup.
- Check for TLS certificate and private key permissions.
- Add a warning if the TLS certificate is expired.
- Add authentication to the Raft portal and add the following related _data_
node configuration options:
<!-- Uncomment with 1.12.x release
- [`[meta].raft-portal-auth-required`](/enterprise_influxdb/v1/administration/configure/config-data-nodes/#raft-portal-auth-required)
- [`[meta].raft-dialer-auth-required`](/enterprise_influxdb/v1/administration/configure/config-data-nodes/#raft-dialer-auth-required)
-->
<!-- TODO: Remove with 1.12.x release: -->
- `[meta].raft-portal-auth-required`
- `[meta].raft-dialer-auth-required`
- Improve error handling.
- InfluxQL updates:
- Delete series by retention policy.
<!-- TODO: Uncomment with 1.12.x release:
- Allow retention policies to discard writes that fall within their range, but
outside of [`FUTURE LIMIT`](/enterprise_influxdb/v1/query_language/manage-database/#future-limit)
and [`PAST LIMIT`](/enterprise_influxdb/v1/query_language/manage-database/#past-limit).
-->
<!-- TODO: Remove with 1.12.x release: -->
- Allow retention policies to discard writes that fall within their range, but
outside of `FUTURE LIMIT` and `PAST LIMIT`.
## Bug fixes

View File

@ -326,7 +326,7 @@ Very useful for troubleshooting, but will log any sensitive data contained withi
Environment variable: `INFLUXDB_DATA_QUERY_LOG_ENABLED`
#### query-log-path
#### query-log-path {metadata="v1.12.0+"}
Default is `""`.
@ -352,7 +352,7 @@ The following is an example of a `logrotate` configuration:
```
Environment variable: `INFLUXDB_DATA_QUERY_LOG_PATH`
-->
#### wal-fsync-delay
Default is `"0s"`.

View File

@ -306,7 +306,7 @@ See
[Shard group duration management](/enterprise_influxdb/v1/concepts/schema_and_data_layout/#shard-group-duration-management)
for recommended configurations.
##### `PAST LIMIT`
##### `PAST LIMIT` {metadata="v1.12.0+"}
The `PAST LIMIT` clause defines a time boundary before and relative to _now_
in which points written to the retention policy are accepted. If a point has a
@ -317,7 +317,7 @@ For example, if a write request tries to write data to a retention policy with a
`PAST LIMIT 6h` and there are points in the request with timestamps older than
6 hours, those points are rejected.
##### `FUTURE LIMIT`
##### `FUTURE LIMIT` {metadata="v1.12.0+"}
The `FUTURE LIMIT` clause defines a time boundary after and relative to _now_
in which points written to the retention policy are accepted. If a point has a

View File

@ -453,7 +453,7 @@ Default value is `$HOME/.influxdb/wal`.
See the [file system layout](/enterprise_influxdb/v1/concepts/file-system-layout/#file-system-layout)
for InfluxDB on your system.
##### [ `-tsmfile <tsm_file>` ]
##### [ `-tsmfile <tsm_file>` ] {metadata="v1.12.0+"}
Path to a single tsm file to export. This requires both `-database` and
`-retention` to be specified.
@ -472,7 +472,7 @@ influx_inspect export -compress
influx_inspect export -database DATABASE_NAME -retention RETENTION_POLICY
```
##### Export data from a single TSM file
##### Export data from a single TSM file {metadata="v1.12.0+"}
```bash
influx_inspect export \

View File

@ -44,6 +44,8 @@ ID Database Retention Policy Desired Replicas Shard Group Start
{{% /expand %}}
{{< /expand-wrapper >}}
#### Show inconsistent shards {metadata="v1.12.0+"}
You can also use the `-m` flag to output "inconsistent" shards which are shards
that are either in metadata but not on disk or on disk but not in metadata.
@ -52,10 +54,8 @@ that are either in metadata but not on disk or on disk but not in metadata.
| Flag | Description |
| :--- | :-------------------------------- |
| `-v` | Return detailed shard information |
| `-m` | Return inconsistent shards |
| `-m` | Return inconsistent shards |
{{% caption %}}
_Also see [`influxd-ctl` global flags](/enterprise_influxdb/v1/tools/influxd-ctl/#influxd-ctl-global-flags)._
{{% /caption %}}
## Examples

View File

@ -16,4 +16,4 @@ source: /shared/influxdb-v2/write-data/replication/replicate-data.md
---
<!-- The content of this file is at
// SOURCE content/shared/influxdb-v2/write-data/replication/replicate-data.md-->
// SOURCE content/shared/influxdb-v2/write-data/replication/replicate-data.md -->

View File

@ -10,27 +10,50 @@ aliases:
- /influxdb/v1/about_the_project/releasenotes-changelog/
alt_links:
v2: /influxdb/v2/reference/release-notes/influxdb/
enterprise_v1: /enterprise_influxdb/v1/about-the-project/release-notes/
---
## v1.12.1 {date="2025-06-26"}
## v1.12.x {date="TBD"}
> [!Important]
> #### Pre-release documentation
>
> This release is not yet available. [**v{{% latest-patch %}}**](#v1118) is the latest InfluxDB v1 release.
## Features
- Add additional log output when using
[`influx_inspect buildtsi`](/influxdb/v1/tools/influx_inspect/#buildtsi) to
rebuild the TSI index.
<!-- TODO: Uncomment with 1.12.x release:
- Use [`influx_inspect export`](/influxdb/v1/tools/influx_inspect/#export) with
[`-tsmfile` option](/influxdb/v1/tools/influx_inspect/#--tsmfile-tsm_file-) to
export a single TSM file.
-->
<!-- TODO: Remove with 1.12.x release: -->
- Use [`influx_inspect export`](/influxdb/v1/tools/influx_inspect/#export) with
`-tsmfile` option to
export a single TSM file.
- Add `fluxQueryRespBytes` metric to the `/debug/vars` metrics endpoint.
<!-- TODO: Uncomment with 1.12.x release:
- Add [`aggressive-points-per-block` configuration option](/influxdb/v1/administration/config/#aggressive-points-per-block)
to prevent TSM files from not getting fully compacted.
-->
<!-- TODO: Remove with 1.12.x release: -->
- Add `aggressive-points-per-block` configuration option
to prevent TSM files from not getting fully compacted.
- Improve error handling.
- InfluxQL updates:
- Delete series by retention policy.
<!-- TODO: Uncomment with 1.12.x release:
- Allow retention policies to discard writes that fall within their range, but
outside of [`FUTURE LIMIT`](/influxdb/v1/query_language/manage-database/#future-limit)
and [`PAST LIMIT`](/influxdb/v1/query_language/manage-database/#past-limit).
-->
<!-- TODO: Remove with 1.12.x release: -->
- Allow retention policies to discard writes that fall within their range, but
outside of `FUTURE LIMIT` and `PAST LIMIT`.
## Bug fixes

View File

@ -307,7 +307,7 @@ See
[Shard group duration management](/influxdb/v1/concepts/schema_and_data_layout/#shard-group-duration-management)
for recommended configurations.
##### `PAST LIMIT`
##### `PAST LIMIT` {metadata="v1.12.0+"}
The `PAST LIMIT` clause defines a time boundary before and relative to _now_
in which points written to the retention policy are accepted. If a point has a
@ -318,7 +318,7 @@ For example, if a write request tries to write data to a retention policy with a
`PAST LIMIT 6h` and there are points in the request with timestamps older than
6 hours, those points are rejected.
##### `FUTURE LIMIT`
##### `FUTURE LIMIT` {metadata="v1.12.0+"}
The `FUTURE LIMIT` clause defines a time boundary after and relative to _now_
in which points written to the retention policy are accepted. If a point has a

View File

@ -449,7 +449,7 @@ Default value is `$HOME/.influxdb/wal`.
See the [file system layout](/influxdb/v1/concepts/file-system-layout/#file-system-layout)
for InfluxDB on your system.
##### [ `-tsmfile <tsm_file>` ]
##### [ `-tsmfile <tsm_file>` ] {metadata="v1.12.0+"}
Path to a single tsm file to export. This requires both `-database` and
`-retention` to be specified.
@ -468,7 +468,7 @@ influx_inspect export -compress
influx_inspect export -database DATABASE_NAME -retention RETENTION_POLICY
```
##### Export data from a single TSM file
##### Export data from a single TSM file {metadata="v1.12.0+"}
```bash
influx_inspect export \

View File

@ -8,9 +8,11 @@ menu:
parent: Administer InfluxDB Clustered
name: Scale your cluster
weight: 207
influxdb3/clustered/tags: [scale]
influxdb3/clustered/tags: [scale, performance, Kubernetes]
related:
- /influxdb3/clustered/reference/internals/storage-engine/
- /influxdb3/clustered/write-data/best-practices/data-lifecycle/
- /influxdb3/clustered/query-data/troubleshoot-and-optimize/optimize-queries/
- https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits, Kubernetes resource requests and limits
---
@ -559,11 +561,14 @@ concurrency demands or reaches the hardware limits of your underlying nodes.
### Compactor
- **Recommended**: Maintain **1 Compactor pod** and use [vertical scaling](#vertical-scaling) (especially
increasing the available CPU) for the Compactor.
- **Recommended**: Maintain **1 Compactor pod** and use [vertical scaling](#vertical-scaling) for the Compactor.
Scale CPU and memory resources together, as compactor concurrency settings scale based on memory, not CPU count.
- Because compaction is a compute-heavy process, horizontal scaling increases compaction throughput, but not as
efficiently as vertical scaling.
> [!Important]
> When scaling the Compactor, scale CPU and memory resources together.
### Garbage collector
The [Garbage collector](/influxdb3/clustered/reference/internals/storage-engine/#garbage-collector) is a lightweight process that typically doesn't require

View File

@ -18,6 +18,7 @@ prepend: |
> [!Note]
> InfluxDB 3 Core is purpose-built for real-time data monitoring and recent data.
> InfluxDB 3 Enterprise builds on top of Core with support for historical data
> analysis and extended features.
> querying, high availability, read replicas, and more.
> Enterprise will soon unlock
> enhanced security, row-level deletions, an administration UI, and more.

View File

@ -704,7 +704,7 @@ name: data
## ATAN2()
Returns the the arctangent of `y/x` in radians.
Returns the arctangent of `y/x` in radians.
### Basic syntax
@ -1609,7 +1609,7 @@ SELECT DERIVATIVE(<function> ([ * | <field_key> | /<regular_expression>/ ]) [ ,
The advanced syntax requires a [`GROUP BY time()` clause](/influxdb/version/query-data/influxql/explore-data/group-by/#group-by-time-intervals) and a nested InfluxQL function.
The query first calculates the results for the nested function at the specified `GROUP BY time()` interval and then applies the `DERIVATIVE()` function to those results.
The `unit` argument is an integer followed by a [duration](//influxdb/version/reference/glossary/#duration) and it is optional.
The `unit` argument is an integer followed by a [duration](/influxdb/version/reference/glossary/#duration) and it is optional.
If the query does not specify the `unit` the `unit` defaults to the `GROUP BY time()` interval.
Note that this behavior is different from the [basic syntax's](#basic-syntax-1) default behavior.

View File

@ -1,9 +1,9 @@
Use InfluxDB replication streams (InfluxDB Edge Data Replication) to replicate
the incoming data of select buckets to one or more buckets on a remote
InfluxDB OSS, InfluxDB Cloud, or InfluxDB Enterprise instance.
InfluxDB OSS, InfluxDB Cloud, or InfluxDB Enterprise v1 instance.
Replicate data from InfluxDB OSS to InfluxDB Cloud, InfluxDB OSS, or InfluxDB Enterprise.
Replicate data from InfluxDB OSS to InfluxDB Cloud, InfluxDB OSS, or InfluxDB Enterprise v1.
- [Configure a replication stream](#configure-a-replication-stream)
- [Replicate downsampled or processed data](#replicate-downsampled-or-processed-data)
@ -17,10 +17,9 @@ Use the [`influx` CLI](/influxdb/version/tools/influx-cli/) or the
[InfluxDB {{< current-version >}} API](/influxdb/version/reference/api/) to configure
a replication stream.
{{% note %}}
To replicate data to InfluxDB OSS or InfluxDB Enterprise, adjust the
remote connection values accordingly.
{{% /note %}}
> [!Note]
> To replicate data to InfluxDB OSS or InfluxDB Enterprise v1, adjust the
> remote connection values accordingly.
{{< tabs-wrapper >}}
{{% tabs %}}
@ -30,156 +29,202 @@ remote connection values accordingly.
{{% tab-content %}}
<!--------------------------------- BEGIN CLI --------------------------------->
### Step 1: Create or find a remote connection
1. In your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS instance, use
the `influx remote create` command to create a remote connection to replicate data to.
- [Create a remote connection](#create-a-remote-connection-cli)
- [Use an existing remote connection](#use-an-existing-remote-connection-cli)
**Provide the following:**
#### Create a remote connection (CLI)
- Remote connection name
{{% show-in "v2" %}}- Remote InfluxDB instance URL{{% /show-in %}}
{{% show-in "v2" %}}- Remote InfluxDB API token _(API token must have write access to the target bucket)_{{% /show-in %}}
{{% show-in "v2" %}}- Remote InfluxDB organization ID{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}- [InfluxDB Cloud region URL](/influxdb/cloud/reference/regions/){{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}- InfluxDB Cloud API token _(API token must have write access to the target bucket)_{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}- InfluxDB Cloud organization ID{{% /show-in %}}
In your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS instance, use
the `influx remote create` command and provide the following arguments for the remote instance:
```sh
influx remote create \
--name example-remote-name \
--remote-url https://cloud2.influxdata.com \
--remote-api-token mYsuP3r5Ecr37t0k3n \
--remote-org-id 00xoXXoxXX00
```
{{% show-in "v2" %}}
- Remote connection name
- Remote InfluxDB instance URL
- Remote InfluxDB API token _(API token must have write access to the target bucket)_
- Remote InfluxDB organization ID
{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}
- Remote connection name
- [InfluxDB Cloud region URL](/influxdb/cloud/reference/regions/)
- InfluxDB Cloud API token _(API token must have write access to the target bucket)_
- InfluxDB Cloud organization ID
{{% /show-in %}}
If you already have remote InfluxDB connections configured, you can use an existing connection. To view existing connections, run `influx remote list`.
```sh
influx remote create \
--name example-remote-name \
--remote-url https://cloud2.influxdata.com \
--remote-api-token mYsuP3r5Ecr37t0k3n \
--remote-org-id 00xoXXoxXX00
```
2. In your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS instance, use the
`influx replication create` command to create a replication stream.
#### Use an existing remote connection (CLI)
Alternatively, you can use an existing connection that you have already configured.
To retrieve existing connections, run `influx remote list`.
### Step 2: Create a replication stream (CLI)
In your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS instance, use the
`influx replication create` command and provide the following arguments:
**Provide the following:**
{{% show-in "v2" %}}
- Replication stream name
- Remote connection ID (created in the previous step)
- Local bucket ID to replicate writes from
- Remote bucket name or ID to replicate writes to. If replicating to **InfluxDB Enterprise v1**, use the `db-name/rp-name` bucket name syntax.{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}
- Replication stream name
- Remote connection ID (created in the previous step)
- InfluxDB OSS bucket ID to replicate writes from
- InfluxDB Cloud bucket ID to replicate writes to
{{% /show-in %}}
- Replication stream name
{{% show-in "v2" %}}- Remote connection ID{{% /show-in %}}
{{% show-in "v2" %}}- Local bucket ID to replicate writes from{{% /show-in %}}
{{% show-in "v2" %}}- Remote bucket name or ID to replicate writes to. If replicating to **InfluxDB Enterprise**, use the `db-name/rp-name` bucket name syntax.{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}- Remote connection ID{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}- InfluxDB OSS bucket ID to replicate writes from{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}- InfluxDB Cloud bucket ID to replicate writes to{{% /show-in %}}
```sh
influx replication create \
--name REPLICATION_STREAM_NAME \
--remote-id REPLICATION_REMOTE_ID \
--local-bucket-id INFLUX_BUCKET_ID \
--remote-bucket REMOTE_INFLUX_BUCKET_NAME
```
```sh
influx replication create \
--name REPLICATION_STREAM_NAME \
--remote-id REPLICATION_REMOTE_ID \
--local-bucket-id INFLUX_BUCKET_ID \
--remote-bucket REMOTE_INFLUX_BUCKET_NAME
```
Once a replication stream is created, InfluxDB {{% show-in "v2" %}}OSS{{% /show-in %}}
will replicate all writes to the specified bucket to the {{% show-in "v2" %}}remote {{% /show-in %}}
After you create the replication stream, InfluxDB {{% show-in "v2" %}}OSS{{% /show-in %}}
replicates all writes to the specified local bucket to the {{% show-in "v2" %}}remote {{% /show-in %}}
InfluxDB {{% show-in "cloud,cloud-serverless" %}}Cloud {{% /show-in %}}bucket.
Use the `influx replication list` command to view information such as the current queue size,
max queue size, and latest status code.
<!---------------------------------- END CLI ---------------------------------->
{{% /tab-content %}}
{{% tab-content %}}
<!--------------------------------- BEGIN API --------------------------------->
1. Send a `POST` request to your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS `/api/v2/remotes` endpoint to create a remote connection to replicate data to.
### Step 1: Create or find a remote connection (API)
{{< keep-url >}}
{{< api-endpoint endpoint="localhost:8086/api/v2/remotes" method="POST" api-ref="/influxdb/version/api/#operation/PostRemoteConnection" >}}
- [Create a remote connection](#create-a-remote-connection-api)
- [Use an existing remote connection](#use-an-existing-remote-connection-api)
Include the following in your request:
#### Create a remote connection (API)
- **Request method:** `POST`
- **Headers:**
- **Authorization:** `Token` scheme with your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS [API token](/influxdb/version/admin/tokens/)
- **Content-type:** `application/json`
- **Request body:** JSON object with the following fields:
{{< req type="key" >}}
- {{< req "\*" >}} **allowInsecureTLS:** All insecure TLS connections
- **description:** Remote description
- {{< req "\*" >}} **name:** Remote connection name
- {{< req "\*" >}} **orgID:** {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS organization ID
{{% show-in "v2" %}}- {{< req "\*" >}} **remoteAPIToken:** Remote InfluxDB API token _(API token must have write access to the target bucket)_{{% /show-in %}}
{{% show-in "v2" %}}- {{< req "\*" >}} **remoteOrgID:** Remote InfluxDB organization ID{{% /show-in %}}
{{% show-in "v2" %}}- {{< req "\*" >}} **remoteURL:** Remote InfluxDB instance URL{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}- {{< req "\*" >}} **remoteAPIToken:** InfluxDB Cloud API token _(API token must have write access to the target bucket)_{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}- {{< req "\*" >}} **remoteOrgID:** InfluxDB Cloud organization ID{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}- {{< req "\*" >}} **remoteURL:** [InfluxDB Cloud region URL](/influxdb/cloud/reference/regions/){{% /show-in %}}
To create a remote connection to replicate data to,
send a `POST` request to your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS `/api/v2/remotes` endpoint:
{{< keep-url >}}
```sh
curl --request POST http://localhost:8086/api/v2/remotes \
--header 'Authorization: Token INFLUX_OSS_TOKEN' \
--data '{
"allowInsecureTLS": false,
"description": "Example remote description",
"name": "Example remote name",
"orgID": "INFLUX_OSS_ORG_ID",
"remoteAPIToken": "REMOTE_INFLUX_TOKEN",
"remoteOrgID": "REMOTE_INFLUX_ORG_ID",
"remoteURL": "https://cloud2.influxdata.com"
}'
```
{{< keep-url >}}
{{< api-endpoint endpoint="localhost:8086/api/v2/remotes" method="POST" api-ref="/influxdb/version/api/#operation/PostRemoteConnection" >}}
If you already have remote InfluxDB connections configured, you can use an
existing connection. To view existing connections, use the `/api/v2/remotes`
endpoint with the `GET` request method.
Include the following parameters in your request:
{{< keep-url >}}
{{< api-endpoint endpoint="localhost:8086/api/v2/remotes" method="GET" api-ref="/influxdb/version/api/#operation/GetRemoteConnections" >}}
- **Request method:** `POST`
- **Headers:**
- **Authorization:** `Token` scheme with your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS [API token](/influxdb/version/admin/tokens/)
- **Content-type:** `application/json`
{{% show-in "v2" %}}
- **Request body:** JSON object with the following fields:
{{< req type="key" >}}
- {{< req "\*" >}} **allowInsecureTLS:** All insecure TLS connections
- **description:** Remote description
- {{< req "\*" >}} **name:** Remote connection name
- {{< req "\*" >}} **orgID:** {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS organization ID
- {{< req "\*" >}} **remoteAPIToken:** Remote InfluxDB API token _(API token must have write access to the target bucket)_
- {{< req "\*" >}} **remoteOrgID:** Remote InfluxDB organization ID
- {{< req "\*" >}} **remoteURL:** Remote InfluxDB instance URL
{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}
- **Request body:** JSON object with the following fields:
{{< req type="key" >}}
- {{< req "\*" >}} **allowInsecureTLS:** All insecure TLS connections
- **description:** Remote description
- {{< req "\*" >}} **name:** Remote connection name
- {{< req "\*" >}} **orgID:** {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS organization ID
- {{< req "\*" >}} **remoteAPIToken:** InfluxDB Cloud API token _(API token must have write access to the target bucket)_
- {{< req "\*" >}} **remoteOrgID:** InfluxDB Cloud organization ID
- {{< req "\*" >}} **remoteURL:** [InfluxDB Cloud region URL](/influxdb/cloud/reference/regions/)
{{% /show-in %}}
Include the following in your request:
{{< keep-url >}}
```sh
curl --request POST http://localhost:8086/api/v2/remotes \
--header 'Authorization: Token INFLUX_OSS_TOKEN' \
--data '{
"allowInsecureTLS": false,
"description": "Example remote description",
"name": "Example remote name",
"orgID": "INFLUX_OSS_ORG_ID",
"remoteAPIToken": "REMOTE_INFLUX_TOKEN",
"remoteOrgID": "REMOTE_INFLUX_ORG_ID",
"remoteURL": "https://cloud2.influxdata.com"
}'
```
- **Request method:** `GET`
- **Headers:**
- **Authorization:** `Token` scheme with your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS [API token](/influxdb/version/admin/tokens/)
- **Query parameters:**
- **orgID:** {{% show-in "v2" %}}Local{{% /show-in %}} InfluxDB OSS organization ID
#### Use an existing remote connection (API)
{{< keep-url >}}
```sh
curl --request GET \
http://localhost:8086/api/v2/remotes?orgID=INFLUX_OSS_ORG_ID \
--header 'Authorization: Token INFLUX_OSS_TOKEN' \
```
Alternatively, you can use an
existing connection that you have already configured.
To retrieve existing connections, use the `/api/v2/remotes`
endpoint with the `GET` request method:
2. Send a `POST` request to your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS
`/api/v2/replications` endpoint to create a replication stream.
{{< keep-url >}}
{{< api-endpoint endpoint="localhost:8086/api/v2/remotes" method="GET" api-ref="/influxdb/version/api/#operation/GetRemoteConnections" >}}
{{< keep-url >}}
{{< api-endpoint endpoint="localhost:8086/api/v2/remotes" method="POST" api-ref="/influxdb/version/api/#operation/PostRemoteConnection" >}}
Include the following in your request:
Include the following parameters in your request:
- **Request method:** `POST`
- **Headers:**
- **Authorization:** `Token` scheme with your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS [API token](/influxdb/version/admin/tokens/)
- **Content-type:** `application/json`
- **Request body:** JSON object with the following fields:
{{< req type="key" >}}
- **dropNonRetryableData:** Drop data when a non-retryable error is encountered.
- {{< req "\*" >}} **localBucketID:** {{% show-in "v2" %}}Local{{% /show-in %}} InfluxDB OSS bucket ID to replicate writes from.
- {{< req "\*" >}} **maxAgeSeconds:** Maximum age of data in seconds before it is dropped (default is `604800`, must be greater than or equal to `0`).
- {{< req "\*" >}} **maxQueueSizeBytes:** Maximum replication queue size in bytes (default is `67108860`, must be greater than or equal to `33554430`).
- {{< req "\*" >}} **name:** Replication stream name.
- {{< req "\*" >}} **orgID:** {{% show-in "v2" %}}Local{{% /show-in %}} InfluxDB OSS organization ID.
{{% show-in "v2" %}}- {{< req "\*" >}} **remoteBucketID:** Remote bucket ID to replicate writes to.{{% /show-in %}}
{{% show-in "v2" %}}- {{< req "\*" >}} **remoteBucketName:** Remote bucket name to replicate writes to. If replicating to **InfluxDB Enterprise**, use the `db-name/rp-name` bucket name syntax.{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}- {{< req "\*" >}} **remoteBucketID:** InfluxDB Cloud bucket ID to replicate writes to.{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}- {{< req "\*" >}} **remoteBucketName:** InfluxDB Cloud bucket name to replicate writes to.{{% /show-in %}}
- {{< req "\*" >}} **remoteID:** Remote connection ID
- **Headers:**
- **Authorization:** `Token` scheme with your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS [API token](/influxdb/version/admin/tokens/)
- **Query parameters:**
- **orgID:** {{% show-in "v2" %}}Local{{% /show-in %}} InfluxDB OSS organization ID
{{% note %}}
`remoteBucketID` and `remoteBucketName` are mutually exclusive.
{{% show-in "v2" %}}If replicating to **InfluxDB Enterprise**, use `remoteBucketName` with the `db-name/rp-name` bucket name syntax.{{% /show-in %}}
{{% /note %}}
{{< keep-url >}}
```sh
curl --request GET \
http://localhost:8086/api/v2/remotes?orgID=INFLUX_OSS_ORG_ID \
--header 'Authorization: Token INFLUX_OSS_TOKEN' \
```
### Step 2: Create a replication stream (API)
Send a `POST` request to your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS
`/api/v2/replications` endpoint to create a replication stream.
{{< keep-url >}}
{{< api-endpoint endpoint="localhost:8086/api/v2/remotes" method="POST" api-ref="/influxdb/version/api/#operation/PostRemoteConnection" >}}
Include the following parameters in your request:
- **Headers:**
- **Authorization:** `Token` scheme with your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS [API token](/influxdb/version/admin/tokens/)
- **Content-type:** `application/json`
{{% show-in "v2" %}}
- **Request body:** JSON object with the following fields:
{{< req type="key" >}}
- **dropNonRetryableData:** Drop data when a non-retryable error is encountered.
- {{< req "\*" >}} **localBucketID:** Local InfluxDB OSS bucket ID to replicate writes from.
- {{< req "\*" >}} **maxAgeSeconds:** Maximum age of data in seconds before it is dropped (default is `604800`, must be greater than or equal to `0`).
- {{< req "\*" >}} **maxQueueSizeBytes:** Maximum replication queue size in bytes (default is `67108860`, must be greater than or equal to `33554430`).
- {{< req "\*" >}} **name:** Replication stream name.
- {{< req "\*" >}} **orgID:** Local InfluxDB OSS organization ID.
- {{< req "\*" >}} **remoteBucketID:** Remote bucket ID to replicate writes to.
- {{< req "\*" >}} **remoteBucketName:** Remote bucket name to replicate writes to. If replicating to **InfluxDB Enterprise v1**, use the `db-name/rp-name` bucket name syntax.
{{% /show-in %}}
{{% show-in "cloud,cloud-serverless" %}}
- **Request body:** JSON object with the following fields:
{{< req type="key" >}}
- **dropNonRetryableData:** Drop data when a non-retryable error is encountered
- {{< req "\*" >}} **localBucketID:** InfluxDB OSS bucket ID to replicate writes from
- {{< req "\*" >}} **maxAgeSeconds:** Maximum age of data in seconds before it is dropped (default is `604800`, must be greater than or equal to `0`)
- {{< req "\*" >}} **maxQueueSizeBytes:** Maximum replication queue size in bytes (default is `67108860`, must be greater than or equal to `33554430`)
- {{< req "\*" >}} **name:** Replication stream name
- {{< req "\*" >}} **orgID:** InfluxDB OSS organization ID
- {{< req "\*" >}} **remoteBucketID:** InfluxDB Cloud bucket ID to replicate writes to (mutually exclusive with `remoteBucketName`)
- {{< req "\*" >}} **remoteBucketName:** InfluxDB Cloud bucket name to replicate writes to (mutually exclusive with `remoteBucketID`)
- {{< req "\*" >}} **remoteID:** Remote connection ID
{{% /show-in %}}
> [!Note]
> `remoteBucketID` and `remoteBucketName` are mutually exclusive.
> {{% show-in "v2" %}}If replicating to **InfluxDB Enterprise v1**, use `remoteBucketName` with the `db-name/rp-name` bucket name syntax.{{% /show-in %}}
{{< keep-url >}}
```sh
@ -197,19 +242,18 @@ curl --request POST http://localhost:8086/api/v2/replications \
}'
```
Once a replication stream is created, InfluxDB {{% show-in "v2" %}}OSS{{% /show-in %}}
will replicate all writes from the specified local bucket to the {{% show-in "v2" %}}remote {{% /show-in %}}
After you create a replication stream, InfluxDB {{% show-in "v2" %}}OSS{{% /show-in %}}
replicates all writes from the specified local bucket to the {{% show-in "v2" %}}remote {{% /show-in %}}
InfluxDB {{% show-in "cloud,cloud-serverless" %}}Cloud {{% /show-in %}}bucket.
To get
information such as the current queue size, max queue size, and latest status
code for each replication stream, send a `GET` request to your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS `/api/v2/replications` endpoint.
code for each replication stream, send a `GET` request to your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS `/api/v2/replications` endpoint:
{{< keep-url >}}
{{< api-endpoint endpoint="localhost:8086/api/v2/replications" method="GET" api-ref="/influxdb/version/api/#operation/GetReplications" >}}
Include the following in your request:
Include the following parameters in your request:
- **Request method:** `GET`
- **Headers:**
- **Authorization:** `Token` scheme with your {{% show-in "v2" %}}local{{% /show-in %}} InfluxDB OSS [API token](/influxdb/version/admin/tokens/)
- **Query parameters:**

View File

@ -69,6 +69,59 @@ influxdb3 create distinct_cache \
<!--------------------------- END ENTERPRISE EXAMPLE -------------------------->
{{% /show-in %}}
## Use the HTTP API
To use the HTTP API to create a Distinct Value Cache, send a `POST` request to the `/api/v3/configure/distinct_cache` endpoint.
{{% api-endpoint method="POST" endpoint="/api/v3/configure/distinct_cache" api-ref="/influxdb3/version/api/v3/#operation/PostConfigureDistinctCache" %}}
{{% code-placeholders "(DATABASE|TABLE|DVC)_NAME|AUTH_TOKEN|COLUMNS|MAX_(CARDINALITY|AGE)" %}}
```bash
curl -X POST "https://localhost:8181/api/v3/configure/distinct_cache" \
--header "Authorization: Bearer AUTH_TOKEN" \
--json '{
"db": "DATABASE_NAME",
"table": "TABLE_NAME",
"name": "DVC_NAME",
"columns": ["COLUMNS"],
"max_cardinality": MAX_CARDINALITY,
"max_age": MAX_AGE
}'
```
{{% /code-placeholders %}}
### Example
```bash
curl -X POST "https://localhost:8181/api/v3/configure/distinct_cache" \
--header "Authorization: Bearer 00xoXX0xXXx0000XxxxXx0Xx0xx0" \
--json '{
"db": "example-db",
"table": "wind_data",
"name": "windDistinctCache",
"columns": ["country", "county", "city"],
"max_cardinality": 10000,
"max_age": 86400
}'
```
**Response codes:**
- `201` : Success. The distinct cache has been created.
- `204` : Not created. A distinct cache with this configuration already exists.
- `400` : Bad request.
> [!Note]
> #### API parameter differences
>
> - **Columns format**: The API uses a JSON array (`["country", "county", "city"]`)
> instead of the CLI's comma-delimited format (`country,county,city`).
> - **Maximum age format**: The API uses seconds (`86400`) instead of the CLI's
> [humantime format](https://docs.rs/humantime/latest/humantime/fn.parse_duration.html) (`24h`, `1 day`).
Replace the following:
- {{% code-placeholder-key %}}`DATABASE_NAME`{{% /code-placeholder-key %}}:

View File

@ -31,3 +31,37 @@ FROM
WHERE
country = 'Spain'
```
## Use the HTTP API
To use the HTTP API to query cached data, send a `GET` or `POST` request to the `/api/v3/query_sql` endpoint and include the [`distinct_cache()`](/influxdb3/version/reference/sql/functions/cache/#distinct_cache) function in your query.
{{% api-endpoint method="GET" endpoint="/api/v3/query_sql" api-ref="/influxdb3/version/api/v3/#operation/GetExecuteQuerySQL" %}}
{{% api-endpoint method="POST" endpoint="/api/v3/query_sql" api-ref="/influxdb3/version/api/v3/#operation/PostExecuteQuerySQL" %}}
{{% code-placeholders "DATABASE_NAME|AUTH_TOKEN|TABLE_NAME|CACHE_NAME" %}}
```bash
curl -X POST "https://localhost:8181/api/v3/query_sql" \
--header "Authorization: Bearer AUTH_TOKEN" \
--json '{
"db": "DATABASE_NAME",
"q": "SELECT * FROM distinct_cache('\''TABLE_NAME'\'', '\''CACHE_NAME'\'')",
"format": "json"
}'
```
{{% /code-placeholders %}}
## Example with WHERE clause
```bash
curl -X POST "https://localhost:8181/api/v3/query_sql" \
--header "Authorization: Bearer 00xoXX0xXXx0000XxxxXx0Xx0xx0" \
--json '{
"db": "example-db",
"q": "SELECT room, temp FROM last_cache('\''home'\'', '\''homeCache'\'') WHERE room = '\''Kitchen'\''",
"format": "json"
}'
```

View File

@ -67,3 +67,44 @@ In the examples above, replace the following:
- {{% code-placeholder-key %}}`AUTH_TOKEN`{{% /code-placeholder-key %}}:
your {{< product-name >}} {{% show-in "enterprise" %}}admin {{% /show-in %}}
authentication token
## Use the HTTP API
To use the HTTP API to query and output cache information from the system table, send a `GET` or `POST` request to the `/api/v3/query_sql` endpoint.
{{% api-endpoint method="GET" endpoint="/api/v3/query_sql" api-ref="/influxdb3/version/api/v3/#operation/GetExecuteQuerySQL" %}}
{{% api-endpoint method="POST" endpoint="/api/v3/query_sql" api-ref="/influxdb3/version/api/v3/#operation/PostExecuteQuerySQL" %}}
### Query all caches
{{% code-placeholders "DATABASE_NAME|AUTH_TOKEN" %}}
```bash
curl -X POST "https://localhost:8181/api/v3/query_sql" \
--header "Authorization: Bearer AUTH_TOKEN" \
--json '{
"db": "DATABASE_NAME",
"q": "SELECT * FROM system.distinct_caches",
"format": "json"
}'
```
{{% /code-placeholders %}}
## Query specific cache details
{{% code-placeholders "DATABASE_NAME|AUTH_TOKEN|CACHE_NAME" %}}
```bash
curl -X POST "https://localhost:8181/api/v3/query_sql" \
--header "Authorization: Bearer AUTH_TOKEN" \
--json '{
"db": "DATABASE_NAME",
"q": "SELECT * FROM system.distinct_caches WHERE name = '\''CACHE_NAME'\''",
"format": "json"
}'
```
{{% /code-placeholders %}}

View File

@ -80,6 +80,59 @@ influxdb3 create last_cache \
<!--------------------------- END ENTERPRISE EXAMPLE -------------------------->
{{% /show-in %}}
## Use the HTTP API
To use the HTTP API to create a Last Value Cache, send a `POST` request to the `/api/v3/configure/last_cache` endpoint.
{{% api-endpoint method="POST" endpoint="/api/v3/configure/last_cache" api-ref="/influxdb3/version/api/v3/#operation/PostConfigureLastCache" %}}
{{% code-placeholders "(DATABASE|TABLE|LVC)_NAME|AUTH_TOKEN|(KEY|VALUE)_COLUMNS|COUNT|TTL" %}}
```bash
curl -X POST "https://localhost:8181/api/v3/configure/last_cache" \
--header "Authorization: Bearer AUTH_TOKEN" \
--json '{
"db": "DATABASE_NAME",
"table": "TABLE_NAME",
"name": "LVC_NAME",
"key_columns": ["KEY_COLUMNS"],
"value_columns": ["VALUE_COLUMNS"],
"count": COUNT,
"ttl": TTL
}'
```
{{% /code-placeholders %}}
### Example
```bash
curl -X POST "https://localhost:8181/api/v3/configure/last_cache" \
--header "Authorization: Bearer 00xoXX0xXXx0000XxxxXx0Xx0xx0" \
--json '{
"db": "example-db",
"table": "home",
"name": "homeLastCache",
"key_columns": ["room", "wall"],
"value_columns": ["temp", "hum", "co"],
"count": 5,
"ttl": 14400
}'
```
**Response codes:**
- `201` : Success. Last cache created.
- `400` : Bad request.
- `401` : Unauthorized.
- `404` : Cache not found.
- `409` : Cache already exists.
> [!Note]
> #### API parameter differences
> Column format: The API uses JSON arrays (["room", "wall"]) instead of the CLI's comma-delimited format (room,wall).
> TTL format: The API uses seconds (14400) instead of the CLI's humantime format (4h, 4 hours).
Replace the following:
- {{% code-placeholder-key %}}`DATABASE_NAME`{{% /code-placeholder-key %}}:
@ -116,4 +169,4 @@ The cache imports the distinct values from the table and starts caching them.
>
> The LVC is stored in memory, so it's important to consider the size and persistence
> of the cache. For more information, see
> [Important things to know about the Last Value Cache](/influxdb3/version/admin/last-value-cache/#important-things-to-know-about-the-last-value-cache).
> [Important things to know about the Last Value Cache.](/influxdb3/version/admin/last-value-cache/#important-things-to-know-about-the-last-value-cache)

View File

@ -23,6 +23,33 @@ influxdb3 delete last_cache \
```
{{% /code-placeholders %}}
## Use the HTTP API
To use the HTTP API to delete a Last Value Cache, send a `DELETE` request to the `/api/v3/configure/last_cache` endpoint with query parameters.
{{% api-endpoint method="DELETE" endpoint="/api/v3/configure/last_cache" api-ref="/influxdb3/core/api/v3/#operation/DeleteConfigureLastCache" %}}
{{% code-placeholders "(DATABASE|TABLE|LVC)_NAME|AUTH_TOKEN" %}}
```bash
curl -X DELETE "https://localhost:8181/api/v3/configure/last_cache?db=DATABASE_NAME&table=TABLE_NAME&name=LVC_NAME" \
--header "Authorization: Bearer AUTH_TOKEN"
```
{{% /code-placeholders %}}
## Example
```bash
curl -X DELETE "https://localhost:8181/api/v3/configure/last_cache?db=example-db&table=home&name=homeLastCache" \
--header "Authorization: Bearer 00xoXX0xXXx0000XxxxXx0Xx0xx0"
```
**Response codes:**
- `200` : Success. The last cache has been deleted.
- `400` : Bad request.
- `401` : Unauthorized.
- `404` : Cache not found.
Replace the following:
- {{% code-placeholder-key %}}`DATABASE_NAME`{{% /code-placeholder-key %}}:

View File

@ -66,3 +66,43 @@ In the examples above, replace the following:
- {{% code-placeholder-key %}}`AUTH_TOKEN`{{% /code-placeholder-key %}}:
your {{< product-name >}} {{% show-in "enterprise" %}}admin {{% /show-in %}}
authentication token
## Use the HTTP API
To use the HTTP API to query and output cache information from the system table, send a `GET` or `POST` request to the `/api/v3/query_sql` endpoint.
{{% api-endpoint method="GET" endpoint="/api/v3/query_sql" api-ref="/influxdb3/version/api/v3/#operation/GetExecuteQuerySQL" %}}
{{% api-endpoint method="POST" endpoint="/api/v3/query_sql" api-ref="/influxdb3/version/api/v3/#operation/PostExecuteQuerySQL" %}}
### Query all last value caches
{{% code-placeholders "DATABASE_NAME|AUTH_TOKEN" %}}
```bash
curl -X POST "https://localhost:8181/api/v3/query_sql" \
--header "Authorization: Bearer AUTH_TOKEN" \
--json '{
"db": "DATABASE_NAME",
"q": "SELECT * FROM system.last_caches",
"format": "json"
}'
```
{{% /code-placeholders %}}
## Query specific cache details
{{% code-placeholders "DATABASE_NAME|AUTH_TOKEN|CACHE_NAME" %}}
```bash
curl -X POST "https://localhost:8181/api/v3/query_sql" \
--header "Authorization: Bearer AUTH_TOKEN" \
--json '{
"db": "DATABASE_NAME",
"q": "SELECT * FROM system.last_caches WHERE name = '\''CACHE_NAME'\''",
"format": "json"
}'
```
{{% /code-placeholders %}}

View File

@ -2,14 +2,6 @@ import { defineConfig } from 'cypress';
import { cwd as _cwd } from 'process';
import * as fs from 'fs';
import * as yaml from 'js-yaml';
import {
BROKEN_LINKS_FILE,
FIRST_BROKEN_LINK_FILE,
initializeReport,
readBrokenLinksReport,
saveCacheStats,
saveValidationStrategy,
} from './cypress/support/link-reporter.js';
export default defineConfig({
e2e: {
@ -88,98 +80,6 @@ export default defineConfig({
}
},
// Broken links reporting tasks
initializeBrokenLinksReport() {
return initializeReport();
},
// Special case domains are now handled directly in the test without additional reporting
// This task is kept for backward compatibility but doesn't do anything special
reportSpecialCaseLink(linkData) {
console.log(
`✅ Expected status code: ${linkData.url} (status: ${linkData.status}) is valid for this domain`
);
return true;
},
reportBrokenLink(linkData) {
try {
// Validate link data
if (!linkData || !linkData.url || !linkData.page) {
console.error('Invalid link data provided');
return false;
}
// Read current report
const report = readBrokenLinksReport();
// Find or create entry for this page
let pageReport = report.find((r) => r.page === linkData.page);
if (!pageReport) {
pageReport = { page: linkData.page, links: [] };
report.push(pageReport);
}
// Check if link is already in the report to avoid duplicates
const isDuplicate = pageReport.links.some(
(link) => link.url === linkData.url && link.type === linkData.type
);
if (!isDuplicate) {
// Add the broken link to the page's report
pageReport.links.push({
url: linkData.url,
status: linkData.status,
type: linkData.type,
linkText: linkData.linkText,
});
// Write updated report back to file
fs.writeFileSync(
BROKEN_LINKS_FILE,
JSON.stringify(report, null, 2)
);
// Store first broken link if not already recorded
const firstBrokenLinkExists =
fs.existsSync(FIRST_BROKEN_LINK_FILE) &&
fs.readFileSync(FIRST_BROKEN_LINK_FILE, 'utf8').trim() !== '';
if (!firstBrokenLinkExists) {
// Store first broken link with complete information
const firstBrokenLink = {
url: linkData.url,
status: linkData.status,
type: linkData.type,
linkText: linkData.linkText,
page: linkData.page,
time: new Date().toISOString(),
};
fs.writeFileSync(
FIRST_BROKEN_LINK_FILE,
JSON.stringify(firstBrokenLink, null, 2)
);
console.error(
`🔴 FIRST BROKEN LINK: ${linkData.url} (${linkData.status}) - ${linkData.type} on page ${linkData.page}`
);
}
// Log the broken link immediately to console
console.error(
`❌ BROKEN LINK: ${linkData.url} (${linkData.status}) - ${linkData.type} on page ${linkData.page}`
);
}
return true;
} catch (error) {
console.error(`Error reporting broken link: ${error.message}`);
// Even if there's an error, we want to ensure the test knows there was a broken link
return true;
}
},
// Cache and incremental validation tasks
saveCacheStatistics(stats) {
try {

View File

@ -1,370 +0,0 @@
/// <reference types="cypress" />
describe('Article', () => {
let subjects = Cypress.env('test_subjects')
? Cypress.env('test_subjects')
.split(',')
.filter((s) => s.trim() !== '')
: [];
// Cache will be checked during test execution at the URL level
// Always use HEAD for downloads to avoid timeouts
const useHeadForDownloads = true;
// Set up initialization for tests
before(() => {
// Initialize the broken links report
cy.task('initializeBrokenLinksReport');
// Clean up expired cache entries
cy.task('cleanupCache').then((cleaned) => {
if (cleaned > 0) {
cy.log(`🧹 Cleaned up ${cleaned} expired cache entries`);
}
});
});
// Display cache statistics after all tests complete
after(() => {
cy.task('getCacheStats').then((stats) => {
cy.log('📊 Link Validation Cache Statistics:');
cy.log(` • Cache hits: ${stats.hits}`);
cy.log(` • Cache misses: ${stats.misses}`);
cy.log(` • New entries stored: ${stats.stores}`);
cy.log(` • Hit rate: ${stats.hitRate}`);
cy.log(` • Total validations: ${stats.total}`);
if (stats.total > 0) {
const message = stats.hits > 0
? `✨ Cache optimization saved ${stats.hits} link validations`
: '🔄 No cache hits - all links were validated fresh';
cy.log(message);
}
// Save cache statistics for the reporter to display
cy.task('saveCacheStatsForReporter', {
hitRate: parseFloat(stats.hitRate.replace('%', '')),
cacheHits: stats.hits,
cacheMisses: stats.misses,
totalValidations: stats.total,
newEntriesStored: stats.stores,
cleanups: stats.cleanups
});
});
});
// Helper function to identify download links
function isDownloadLink(href) {
// Check for common download file extensions
const downloadExtensions = [
'.pdf',
'.zip',
'.tar.gz',
'.tgz',
'.rar',
'.exe',
'.dmg',
'.pkg',
'.deb',
'.rpm',
'.xlsx',
'.csv',
'.doc',
'.docx',
'.ppt',
'.pptx',
];
// Check for download domains or paths
const downloadDomains = ['dl.influxdata.com', 'downloads.influxdata.com'];
// Check if URL contains a download extension
const hasDownloadExtension = downloadExtensions.some((ext) =>
href.toLowerCase().endsWith(ext)
);
// Check if URL is from a download domain
const isFromDownloadDomain = downloadDomains.some((domain) =>
href.toLowerCase().includes(domain)
);
// Return true if either condition is met
return hasDownloadExtension || isFromDownloadDomain;
}
// Helper function for handling failed links
function handleFailedLink(url, status, type, redirectChain = '', linkText = '', pageUrl = '') {
// Report the broken link
cy.task('reportBrokenLink', {
url: url + redirectChain,
status,
type,
linkText,
page: pageUrl,
});
// Throw error for broken links
throw new Error(
`BROKEN ${type.toUpperCase()} LINK: ${url} (status: ${status})${redirectChain} on ${pageUrl}`
);
}
// Helper function to test a link with cache integration
function testLink(href, linkText = '', pageUrl) {
// Check cache first
return cy.task('isLinkCached', href).then((isCached) => {
if (isCached) {
cy.log(`✅ Cache hit: ${href}`);
return cy.task('getLinkCache', href).then((cachedResult) => {
if (cachedResult && cachedResult.result && cachedResult.result.status >= 400) {
// Cached result shows this link is broken
handleFailedLink(href, cachedResult.result.status, cachedResult.result.type || 'cached', '', linkText, pageUrl);
}
// For successful cached results, just return - no further action needed
});
} else {
// Not cached, perform actual validation
return performLinkValidation(href, linkText, pageUrl);
}
});
}
// Helper function to perform actual link validation and cache the result
function performLinkValidation(href, linkText = '', pageUrl) {
// Common request options for both methods
const requestOptions = {
failOnStatusCode: true,
timeout: 15000, // Increased timeout for reliability
followRedirect: true, // Explicitly follow redirects
retryOnNetworkFailure: true, // Retry on network issues
retryOnStatusCodeFailure: true, // Retry on 5xx errors
};
if (useHeadForDownloads && isDownloadLink(href)) {
cy.log(`** Testing download link with HEAD: ${href} **`);
return cy.request({
method: 'HEAD',
url: href,
...requestOptions,
}).then((response) => {
// Prepare result for caching
const result = {
status: response.status,
type: 'download',
timestamp: new Date().toISOString()
};
// Check final status after following any redirects
if (response.status >= 400) {
const redirectInfo =
response.redirects && response.redirects.length > 0
? ` (redirected to: ${response.redirects.join(' -> ')})`
: '';
// Cache the failed result
cy.task('setLinkCache', { url: href, result });
handleFailedLink(href, response.status, 'download', redirectInfo, linkText, pageUrl);
} else {
// Cache the successful result
cy.task('setLinkCache', { url: href, result });
}
});
} else {
cy.log(`** Testing link: ${href} **`);
return cy.request({
url: href,
...requestOptions,
}).then((response) => {
// Prepare result for caching
const result = {
status: response.status,
type: 'regular',
timestamp: new Date().toISOString()
};
if (response.status >= 400) {
const redirectInfo =
response.redirects && response.redirects.length > 0
? ` (redirected to: ${response.redirects.join(' -> ')})`
: '';
// Cache the failed result
cy.task('setLinkCache', { url: href, result });
handleFailedLink(href, response.status, 'regular', redirectInfo, linkText, pageUrl);
} else {
// Cache the successful result
cy.task('setLinkCache', { url: href, result });
}
});
}
}
// Test setup validation
it('Test Setup Validation', function () {
cy.log(`📋 Test Configuration:`);
cy.log(` • Test subjects: ${subjects.length}`);
cy.log(` • Cache: URL-level caching with 30-day TTL`);
cy.log(` • Link validation: Internal, anchor, and allowed external links`);
cy.log('✅ Test setup validation completed');
});
subjects.forEach((subject) => {
it(`${subject} has valid internal links`, function () {
// Add error handling for page visit failures
cy.visit(`${subject}`, { timeout: 20000 }).then(() => {
cy.log(`✅ Successfully loaded page: ${subject}`);
});
// Test internal links
cy.get('article, .api-content').then(($article) => {
// Find links without failing the test if none are found
const $links = $article.find('a[href^="/"]');
if ($links.length === 0) {
cy.log('No internal links found on this page');
return;
}
cy.log(`🔍 Testing ${$links.length} internal links on ${subject}`);
// Now test each link
cy.wrap($links).each(($a) => {
const href = $a.attr('href');
const linkText = $a.text().trim();
try {
testLink(href, linkText, subject);
} catch (error) {
cy.log(`❌ Error testing link ${href}: ${error.message}`);
throw error; // Re-throw to fail the test
}
});
});
});
it(`${subject} has valid anchor links`, function () {
cy.visit(`${subject}`).then(() => {
cy.log(`✅ Successfully loaded page for anchor testing: ${subject}`);
});
// Define selectors for anchor links to ignore, such as behavior triggers
const ignoreLinks = ['.tabs a[href^="#"]', '.code-tabs a[href^="#"]'];
const anchorSelector =
'a[href^="#"]:not(' + ignoreLinks.join('):not(') + ')';
cy.get('article, .api-content').then(($article) => {
const $anchorLinks = $article.find(anchorSelector);
if ($anchorLinks.length === 0) {
cy.log('No anchor links found on this page');
return;
}
cy.log(`🔗 Testing ${$anchorLinks.length} anchor links on ${subject}`);
cy.wrap($anchorLinks).each(($a) => {
const href = $a.prop('href');
const linkText = $a.text().trim();
if (href && href.length > 1) {
// Get just the fragment part
const url = new URL(href);
const anchorId = url.hash.substring(1); // Remove the # character
if (!anchorId) {
cy.log(`Skipping empty anchor in ${href}`);
return;
}
// Use DOM to check if the element exists
cy.window().then((win) => {
const element = win.document.getElementById(anchorId);
if (!element) {
cy.task('reportBrokenLink', {
url: `#${anchorId}`,
status: 404,
type: 'anchor',
linkText,
page: subject,
});
cy.log(`⚠️ Missing anchor target: #${anchorId}`);
}
});
}
});
});
});
it(`${subject} has valid external links`, function () {
// Check if we should skip external links entirely
if (Cypress.env('skipExternalLinks') === true) {
cy.log(
'Skipping all external links as configured by skipExternalLinks'
);
return;
}
cy.visit(`${subject}`).then(() => {
cy.log(
`✅ Successfully loaded page for external link testing: ${subject}`
);
});
// Define allowed external domains to test
const allowedExternalDomains = ['github.com', 'kapa.ai'];
// Test external links
cy.get('article, .api-content').then(($article) => {
// Find links without failing the test if none are found
const $links = $article.find('a[href^="http"]');
if ($links.length === 0) {
cy.log('No external links found on this page');
return;
}
cy.log(`🔍 Found ${$links.length} total external links on ${subject}`);
// Filter links to only include allowed domains
const $allowedLinks = $links.filter((_, el) => {
const href = el.getAttribute('href');
try {
const url = new URL(href);
return allowedExternalDomains.some(
(domain) =>
url.hostname === domain || url.hostname.endsWith(`.${domain}`)
);
} catch (urlError) {
cy.log(`⚠️ Invalid URL found: ${href}`);
return false;
}
});
if ($allowedLinks.length === 0) {
cy.log('No links to allowed external domains found on this page');
cy.log(` • Allowed domains: ${allowedExternalDomains.join(', ')}`);
return;
}
cy.log(
`🌐 Testing ${$allowedLinks.length} links to allowed external domains`
);
cy.wrap($allowedLinks).each(($a) => {
const href = $a.attr('href');
const linkText = $a.text().trim();
try {
testLink(href, linkText, subject);
} catch (error) {
cy.log(`❌ Error testing external link ${href}: ${error.message}`);
throw error;
}
});
});
});
});
});

View File

@ -1,215 +0,0 @@
/**
* Link Cache Manager for Cypress Tests
* Manages caching of link validation results at the URL level
*/
import fs from 'fs';
import path from 'path';
import crypto from 'crypto';
const CACHE_VERSION = 'v2';
const CACHE_KEY_PREFIX = 'link-validation';
const LOCAL_CACHE_DIR = path.join(process.cwd(), '.cache', 'link-validation');
/**
* Cache manager for individual link validation results
*/
export class LinkCacheManager {
constructor(options = {}) {
this.localCacheDir = options.localCacheDir || LOCAL_CACHE_DIR;
// Configurable cache TTL - default 30 days
this.cacheTTLDays =
options.cacheTTLDays || parseInt(process.env.LINK_CACHE_TTL_DAYS) || 30;
this.maxAge = this.cacheTTLDays * 24 * 60 * 60 * 1000;
this.ensureLocalCacheDir();
// Track cache statistics
this.stats = {
hits: 0,
misses: 0,
stores: 0,
cleanups: 0
};
}
ensureLocalCacheDir() {
if (!fs.existsSync(this.localCacheDir)) {
fs.mkdirSync(this.localCacheDir, { recursive: true });
}
}
/**
* Generate cache key for a URL
* @param {string} url - The URL to cache
* @returns {string} Cache key
*/
generateCacheKey(url) {
const urlHash = crypto
.createHash('sha256')
.update(url)
.digest('hex')
.substring(0, 16);
return `${CACHE_KEY_PREFIX}-${CACHE_VERSION}-${urlHash}`;
}
/**
* Get cache file path for a URL
* @param {string} url - The URL
* @returns {string} File path
*/
getCacheFilePath(url) {
const cacheKey = this.generateCacheKey(url);
return path.join(this.localCacheDir, `${cacheKey}.json`);
}
/**
* Check if a URL's validation result is cached
* @param {string} url - The URL to check
* @returns {Object|null} Cached result or null
*/
get(url) {
const cacheFile = this.getCacheFilePath(url);
if (!fs.existsSync(cacheFile)) {
this.stats.misses++;
return null;
}
try {
const content = fs.readFileSync(cacheFile, 'utf8');
const cached = JSON.parse(content);
// TTL check
const age = Date.now() - new Date(cached.cachedAt).getTime();
if (age > this.maxAge) {
fs.unlinkSync(cacheFile);
this.stats.misses++;
this.stats.cleanups++;
return null;
}
this.stats.hits++;
return cached;
} catch (error) {
// Clean up corrupted cache
try {
fs.unlinkSync(cacheFile);
this.stats.cleanups++;
} catch (cleanupError) {
// Ignoring cleanup errors as they are non-critical, but logging for visibility
console.warn(`Failed to clean up corrupted cache file: ${cleanupError.message}`);
}
this.stats.misses++;
return null;
}
}
/**
* Store validation result for a URL
* @param {string} url - The URL
* @param {Object} result - Validation result
* @returns {boolean} True if successfully cached, false otherwise
*/
set(url, result) {
const cacheFile = this.getCacheFilePath(url);
const cacheData = {
url,
result,
cachedAt: new Date().toISOString(),
ttl: new Date(Date.now() + this.maxAge).toISOString()
};
try {
fs.writeFileSync(cacheFile, JSON.stringify(cacheData, null, 2));
this.stats.stores++;
return true;
} catch (error) {
console.warn(`Failed to cache validation result for ${url}: ${error.message}`);
return false;
}
}
/**
* Check if a URL is cached and valid
* @param {string} url - The URL to check
* @returns {boolean} True if cached and valid
*/
isCached(url) {
return this.get(url) !== null;
}
/**
* Get cache statistics
* @returns {Object} Cache statistics
*/
getStats() {
const total = this.stats.hits + this.stats.misses;
const hitRate = total > 0 ? (this.stats.hits / total * 100).toFixed(1) : 0;
return {
...this.stats,
total,
hitRate: `${hitRate}%`
};
}
/**
* Clean up expired cache entries
* @returns {number} Number of entries cleaned up
*/
cleanup() {
let cleaned = 0;
try {
const files = fs.readdirSync(this.localCacheDir);
const cacheFiles = files.filter(file =>
file.startsWith(CACHE_KEY_PREFIX) && file.endsWith('.json')
);
for (const file of cacheFiles) {
const filePath = path.join(this.localCacheDir, file);
try {
const content = fs.readFileSync(filePath, 'utf8');
const cached = JSON.parse(content);
const age = Date.now() - new Date(cached.cachedAt).getTime();
if (age > this.maxAge) {
fs.unlinkSync(filePath);
cleaned++;
}
} catch (error) {
console.warn(`Failed to process cache file "${filePath}": ${error.message}`);
// Remove corrupted files
fs.unlinkSync(filePath);
cleaned++;
}
}
} catch (error) {
console.warn(`Cache cleanup failed: ${error.message}`);
}
this.stats.cleanups += cleaned;
return cleaned;
}
}
/**
* Cypress task helper to integrate cache with Cypress tasks
*/
export const createCypressCacheTasks = (options = {}) => {
const cache = new LinkCacheManager(options);
return {
getLinkCache: (url) => cache.get(url),
setLinkCache: ({ url, result }) => cache.set(url, result),
isLinkCached: (url) => cache.isCached(url),
getCacheStats: () => cache.getStats(),
cleanupCache: () => cache.cleanup()
};
};

View File

@ -1,310 +0,0 @@
/**
* Broken Links Reporter
* Handles collecting, storing, and reporting broken links found during tests
*/
import fs from 'fs';
export const BROKEN_LINKS_FILE = '/tmp/broken_links_report.json';
export const FIRST_BROKEN_LINK_FILE = '/tmp/first_broken_link.json';
const SOURCES_FILE = '/tmp/test_subjects_sources.json';
const CACHE_STATS_FILE = '/tmp/cache_statistics.json';
const VALIDATION_STRATEGY_FILE = '/tmp/validation_strategy.json';
/**
* Reads the broken links report from the file system
* @returns {Array} Parsed report data or empty array if file doesn't exist
*/
export function readBrokenLinksReport() {
if (!fs.existsSync(BROKEN_LINKS_FILE)) {
return [];
}
try {
const fileContent = fs.readFileSync(BROKEN_LINKS_FILE, 'utf8');
// Check if the file is empty or contains only an empty array
if (!fileContent || fileContent.trim() === '' || fileContent === '[]') {
return [];
}
// Try to parse the JSON content
try {
const parsedContent = JSON.parse(fileContent);
// Ensure the parsed content is an array
if (!Array.isArray(parsedContent)) {
console.error('Broken links report is not an array');
return [];
}
return parsedContent;
} catch (parseErr) {
console.error(
`Error parsing broken links report JSON: ${parseErr.message}`
);
return [];
}
} catch (err) {
console.error(`Error reading broken links report: ${err.message}`);
return [];
}
}
/**
* Reads the sources mapping file
* @returns {Object} A mapping from URLs to their source files
*/
function readSourcesMapping() {
try {
if (fs.existsSync(SOURCES_FILE)) {
const sourcesData = JSON.parse(fs.readFileSync(SOURCES_FILE, 'utf8'));
return sourcesData.reduce((acc, item) => {
if (item.url && item.source) {
acc[item.url] = item.source;
}
return acc;
}, {});
}
} catch (err) {
console.warn(`Warning: Could not read sources mapping: ${err.message}`);
}
return {};
}
/**
* Read cache statistics from file
* @returns {Object|null} Cache statistics or null if not found
*/
function readCacheStats() {
try {
if (fs.existsSync(CACHE_STATS_FILE)) {
const content = fs.readFileSync(CACHE_STATS_FILE, 'utf8');
return JSON.parse(content);
}
} catch (err) {
console.warn(`Warning: Could not read cache stats: ${err.message}`);
}
return null;
}
/**
* Read validation strategy from file
* @returns {Object|null} Validation strategy or null if not found
*/
function readValidationStrategy() {
try {
if (fs.existsSync(VALIDATION_STRATEGY_FILE)) {
const content = fs.readFileSync(VALIDATION_STRATEGY_FILE, 'utf8');
return JSON.parse(content);
}
} catch (err) {
console.warn(`Warning: Could not read validation strategy: ${err.message}`);
}
return null;
}
/**
* Save cache statistics for reporting
* @param {Object} stats - Cache statistics to save
*/
export function saveCacheStats(stats) {
try {
fs.writeFileSync(CACHE_STATS_FILE, JSON.stringify(stats, null, 2));
} catch (err) {
console.warn(`Warning: Could not save cache stats: ${err.message}`);
}
}
/**
* Save validation strategy for reporting
* @param {Object} strategy - Validation strategy to save
*/
export function saveValidationStrategy(strategy) {
try {
fs.writeFileSync(
VALIDATION_STRATEGY_FILE,
JSON.stringify(strategy, null, 2)
);
} catch (err) {
console.warn(`Warning: Could not save validation strategy: ${err.message}`);
}
}
/**
* Formats and displays the broken links report to the console
* @param {Array} brokenLinksReport - The report data to display
* @returns {number} The total number of broken links found
*/
export function displayBrokenLinksReport(brokenLinksReport = null) {
// If no report provided, read from file
if (!brokenLinksReport) {
brokenLinksReport = readBrokenLinksReport();
}
// Read cache statistics and validation strategy
const cacheStats = readCacheStats();
const validationStrategy = readValidationStrategy();
// Display cache performance first
if (cacheStats) {
console.log('\n📊 Link Validation Cache Performance:');
console.log('=======================================');
console.log(`Cache hit rate: ${cacheStats.hitRate}%`);
console.log(`Cache hits: ${cacheStats.cacheHits}`);
console.log(`Cache misses: ${cacheStats.cacheMisses}`);
console.log(`Total validations: ${cacheStats.totalValidations || cacheStats.cacheHits + cacheStats.cacheMisses}`);
console.log(`New entries stored: ${cacheStats.newEntriesStored || 0}`);
if (cacheStats.cleanups > 0) {
console.log(`Expired entries cleaned: ${cacheStats.cleanups}`);
}
if (cacheStats.totalValidations > 0) {
const message = cacheStats.cacheHits > 0
? `✨ Cache optimization saved ${cacheStats.cacheHits} link validations`
: '🔄 No cache hits - all links were validated fresh';
console.log(message);
}
if (validationStrategy) {
console.log(`Files analyzed: ${validationStrategy.total}`);
console.log(
`Links needing validation: ${validationStrategy.newLinks.length}`
);
}
console.log(''); // Add spacing after cache stats
}
// Check both the report and first broken link file to determine if we have broken links
const firstBrokenLink = readFirstBrokenLink();
// Only report "no broken links" if both checks pass
if (
(!brokenLinksReport || brokenLinksReport.length === 0) &&
!firstBrokenLink
) {
console.log('\n✅ No broken links detected in the validation report');
return 0;
}
// Special case: check if the single broken link file could be missing from the report
if (
firstBrokenLink &&
(!brokenLinksReport || brokenLinksReport.length === 0)
) {
console.error(
'\n⚠ Warning: First broken link record exists but no links in the report.'
);
console.error('This could indicate a reporting issue.');
}
// Load sources mapping
const sourcesMapping = readSourcesMapping();
// Print a prominent header
console.error('\n\n' + '='.repeat(80));
console.error(' 🚨 BROKEN LINKS DETECTED 🚨 ');
console.error('='.repeat(80));
// Show first failing link if available
if (firstBrokenLink) {
console.error('\n🔴 FIRST FAILING LINK:');
console.error(` URL: ${firstBrokenLink.url}`);
console.error(` Status: ${firstBrokenLink.status}`);
console.error(` Type: ${firstBrokenLink.type}`);
console.error(` Page: ${firstBrokenLink.page}`);
if (firstBrokenLink.linkText) {
console.error(
` Link text: "${firstBrokenLink.linkText.substring(0, 50)}${firstBrokenLink.linkText.length > 50 ? '...' : ''}"`
);
}
console.error('-'.repeat(40));
}
let totalBrokenLinks = 0;
brokenLinksReport.forEach((report) => {
console.error(`\n📄 PAGE: ${report.page}`);
// Add source information if available
const source = sourcesMapping[report.page];
if (source) {
console.error(` PAGE CONTENT SOURCE: ${source}`);
}
console.error('-'.repeat(40));
report.links.forEach((link) => {
console.error(`${link.url}`);
console.error(` - Status: ${link.status}`);
console.error(` - Type: ${link.type}`);
if (link.linkText) {
console.error(
` - Link text: "${link.linkText.substring(0, 50)}${link.linkText.length > 50 ? '...' : ''}"`
);
}
console.error('');
totalBrokenLinks++;
});
});
// Print a prominent summary footer
console.error('='.repeat(80));
console.error(`📊 TOTAL BROKEN LINKS FOUND: ${totalBrokenLinks}`);
console.error('='.repeat(80) + '\n');
return totalBrokenLinks;
}
/**
* Reads the first broken link info from the file system
* @returns {Object|null} First broken link data or null if not found
*/
export function readFirstBrokenLink() {
if (!fs.existsSync(FIRST_BROKEN_LINK_FILE)) {
return null;
}
try {
const fileContent = fs.readFileSync(FIRST_BROKEN_LINK_FILE, 'utf8');
// Check if the file is empty or contains whitespace only
if (!fileContent || fileContent.trim() === '') {
return null;
}
// Try to parse the JSON content
try {
return JSON.parse(fileContent);
} catch (parseErr) {
console.error(
`Error parsing first broken link JSON: ${parseErr.message}`
);
return null;
}
} catch (err) {
console.error(`Error reading first broken link: ${err.message}`);
return null;
}
}
/**
* Initialize the broken links report files
* @returns {boolean} True if initialization was successful
*/
export function initializeReport() {
try {
// Create an empty array for the broken links report
fs.writeFileSync(BROKEN_LINKS_FILE, '[]', 'utf8');
// Reset the first broken link file by creating an empty file
// Using empty string as a clear indicator that no broken link has been recorded yet
fs.writeFileSync(FIRST_BROKEN_LINK_FILE, '', 'utf8');
console.debug('🔄 Initialized broken links reporting system');
return true;
} catch (err) {
console.error(`Error initializing broken links report: ${err.message}`);
return false;
}
}

View File

@ -2,34 +2,10 @@
* InfluxData Documentation E2E Test Runner
*
* This script automates running Cypress end-to-end tests for the InfluxData documentation site.
* It handles starting a local Hugo server, mapping content files to their URLs, running Cypress tests,
* It handles starting a local Hugo server, mapping content files to their URLs, and running Cypress tests,
* and reporting broken links.
*
* Usage: node run-e2e-specs.js [file paths...] [--spec test // Display broken links report
const brokenLinksCount = displayBrokenLinksReport();
// Check if we might have special case failures
const hasSpecialCaseFailures =
results &&
results.totalFailed > 0 &&
brokenLinksCount === 0;
if (hasSpecialCaseFailures) {
console.warn(
` Note: Tests failed (${results.totalFailed}) but no broken links were reported. This may be due to special case URLs (like Reddit) that return expected status codes.`
);
}
if (
(results && results.totalFailed && results.totalFailed > 0 && !hasSpecialCaseFailures) ||
brokenLinksCount > 0
) {
console.error(
`⚠️ Tests failed: ${results.totalFailed || 0} test(s) failed, ${brokenLinksCount || 0} broken links found`
);
cypressFailed = true;
exitCode = 1; *
* Example: node run-e2e-specs.js content/influxdb/v2/write-data.md --spec cypress/e2e/content/article-links.cy.js
* Usage: node run-e2e-specs.js [file paths...] [--spec test specs...]
*/
import { spawn } from 'child_process';
@ -39,7 +15,6 @@ import path from 'path';
import cypress from 'cypress';
import net from 'net';
import { Buffer } from 'buffer';
import { displayBrokenLinksReport, initializeReport } from './link-reporter.js';
import {
HUGO_ENVIRONMENT,
HUGO_PORT,
@ -119,7 +94,7 @@ async function main() {
let exitCode = 0;
let hugoStarted = false;
// (Lines 124-126 removed; no replacement needed)
// (Lines 124-126 removed; no replacement needed)
// Add this signal handler to ensure cleanup on unexpected termination
const cleanupAndExit = (code = 1) => {
@ -364,10 +339,6 @@ async function main() {
// 4. Run Cypress tests
let cypressFailed = false;
try {
// Initialize/clear broken links report before running tests
console.log('Initializing broken links report...');
initializeReport();
console.log(`Running Cypress tests for ${urlList.length} URLs...`);
// Add CI-specific configuration
@ -426,19 +397,13 @@ async function main() {
clearInterval(hugoHealthCheckInterval);
}
// Process broken links report
const brokenLinksCount = displayBrokenLinksReport();
// Determine why tests failed
const testFailureCount = results?.totalFailed || 0;
if (testFailureCount > 0 && brokenLinksCount === 0) {
if (testFailureCount > 0) {
console.warn(
` Note: ${testFailureCount} test(s) failed but no broken links were detected in the report.`
);
console.warn(
' This usually indicates test errors unrelated to link validation.'
);
// Provide detailed failure analysis
if (results) {
@ -531,14 +496,8 @@ async function main() {
// but we'll still report other test failures
cypressFailed = true;
exitCode = 1;
} else if (brokenLinksCount > 0) {
console.error(
`⚠️ Tests failed: ${brokenLinksCount} broken link(s) detected`
);
cypressFailed = true;
exitCode = 1;
} else if (results) {
console.log('✅ Tests completed successfully');
console.log('✅ e2e tests completed successfully');
}
} catch (err) {
console.error(`❌ Cypress execution error: ${err.message}`);
@ -609,9 +568,6 @@ async function main() {
console.error(' • Check if test URLs are accessible manually');
console.error(' • Review Cypress screenshots/videos if available');
// Still try to display broken links report if available
displayBrokenLinksReport();
cypressFailed = true;
exitCode = 1;
} finally {

View File

@ -100,7 +100,7 @@ influxdb:
latest: v2.7
latest_patches:
v2: 2.7.12
v1: 1.12.1
v1: 1.11.8
latest_cli:
v2: 2.7.5
ai_sample_questions:
@ -157,7 +157,7 @@ chronograf:
versions: [v1]
latest: v1.10
latest_patches:
v1: 1.10.7
v1: 1.10.8
ai_sample_questions:
- How do I configure Chronograf for InfluxDB v1?
- How do I create a dashboard in Chronograf?
@ -183,9 +183,9 @@ enterprise_influxdb:
menu_category: self-managed
list_order: 5
versions: [v1]
latest: v1.12
latest: v1.11
latest_patches:
v1: 1.12.1
v1: 1.11.8
ai_sample_questions:
- How can I configure my InfluxDB v1 Enterprise server?
- How do I replicate data between InfluxDB v1 Enterprise and OSS?

View File

@ -111,16 +111,6 @@ pre-push:
node cypress/support/run-e2e-specs.js --spec "cypress/e2e/content/article-links.cy.js" content/example.md
exit $?
# Link validation runs in GitHub actions.
# You can still run it locally for development.
# e2e-links:
# tags: test,links
# glob: 'content/*.{md,html}'
# run: |
# echo "Running link checker for: {staged_files}"
# yarn test:links {staged_files}
# exit $?
# Manage Docker containers
prune-legacy-containers:
priority: 1

View File

@ -55,15 +55,6 @@
"test:codeblocks:v2": "docker compose run --rm --name v2-pytest v2-pytest",
"test:codeblocks:stop-monitors": "./test/scripts/monitor-tests.sh stop cloud-dedicated-pytest && ./test/scripts/monitor-tests.sh stop clustered-pytest",
"test:e2e": "node cypress/support/run-e2e-specs.js",
"test:links": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/article-links.cy.js\"",
"test:links:v1": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/article-links.cy.js\" content/influxdb/{v1,enterprise_influxdb}/**/*.{md,html}",
"test:links:v2": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/article-links.cy.js\" content/influxdb/{cloud,v2}/**/*.{md,html}",
"test:links:v3": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/article-links.cy.js\" content/influxdb3/**/*.{md,html}",
"test:links:chronograf": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/article-links.cy.js\" content/chronograf/**/*.{md,html}",
"test:links:kapacitor": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/article-links.cy.js\" content/kapacitor/**/*.{md,html}",
"test:links:telegraf": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/article-links.cy.js\" content/telegraf/**/*.{md,html}",
"test:links:shared": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/article-links.cy.js\" content/shared/**/*.{md,html}",
"test:links:api-docs": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/article-links.cy.js\" /influxdb3/core/api/,/influxdb3/enterprise/api/,/influxdb3/cloud-dedicated/api/,/influxdb3/cloud-dedicated/api/v1/,/influxdb/cloud-dedicated/api/v1/,/influxdb/cloud-dedicated/api/management/,/influxdb3/cloud-dedicated/api/management/",
"test:shortcode-examples": "node cypress/support/run-e2e-specs.js --spec \"cypress/e2e/content/article-links.cy.js\" content/example.md",
"audit:cli": "node ./helper-scripts/influxdb3-monolith/audit-cli-documentation.js both local",
"audit:cli:3core": "node ./helper-scripts/influxdb3-monolith/audit-cli-documentation.js core local",