Skip to content

Commit 87cd2a0

Browse files
committed
Merge branch 'main' into release-rs-gilboa
2 parents 05a2afe + ea3c563 commit 87cd2a0

File tree

4 files changed

+1981
-493
lines changed

4 files changed

+1981
-493
lines changed

build/add_cmds.py

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import json
4+
import logging
5+
import os
6+
import sys
7+
8+
from components.syntax import Command
9+
from components.markdown import Markdown
10+
11+
12+
def command_filename(name: str) -> str:
13+
"""Convert command name to filename format."""
14+
return name.lower().replace(' ', '-')
15+
16+
17+
def parse_args() -> argparse.Namespace:
18+
parser = argparse.ArgumentParser(description='Creates new Redis command pages from JSON input')
19+
parser.add_argument('json_file', type=str,
20+
help='Path to JSON file containing command definitions')
21+
parser.add_argument('--loglevel', type=str,
22+
default='INFO',
23+
help='Python logging level (overwrites LOGLEVEL env var)')
24+
return parser.parse_args()
25+
26+
27+
def validate_json_structure(data: dict, filename: str) -> None:
28+
"""Validate that the JSON has the expected structure for Redis commands."""
29+
if not isinstance(data, dict):
30+
raise ValueError(f"JSON file {filename} must contain a dictionary at root level")
31+
32+
for command_name, command_data in data.items():
33+
if not isinstance(command_data, dict):
34+
raise ValueError(f"Command '{command_name}' must be a dictionary")
35+
36+
# Check for required fields
37+
required_fields = ['summary', 'since', 'group']
38+
for field in required_fields:
39+
if field not in command_data:
40+
logging.warning(f"Command '{command_name}' missing recommended field: {field}")
41+
42+
# Validate arguments structure if present
43+
if 'arguments' in command_data:
44+
if not isinstance(command_data['arguments'], list):
45+
raise ValueError(f"Command '{command_name}' arguments must be a list")
46+
47+
48+
def load_and_validate_json(filepath: str) -> dict:
49+
"""Load and validate the JSON file containing command definitions."""
50+
if not os.path.exists(filepath):
51+
raise FileNotFoundError(f"JSON file not found: {filepath}")
52+
53+
try:
54+
with open(filepath, 'r') as f:
55+
data = json.load(f)
56+
except json.JSONDecodeError as e:
57+
raise ValueError(f"Invalid JSON in file {filepath}: {e}")
58+
59+
validate_json_structure(data, filepath)
60+
return data
61+
62+
63+
def add_standard_categories(fm_data: dict) -> None:
64+
"""Add the standard categories from create.sh script."""
65+
standard_categories = [
66+
'docs', 'develop', 'stack', 'oss', 'rs', 'rc', 'oss', 'kubernetes', 'clients'
67+
]
68+
fm_data['categories'] = standard_categories
69+
70+
71+
def get_full_command_name(command_name: str, command_data: dict) -> str:
72+
"""Get the full command name, handling container commands."""
73+
container = command_data.get('container')
74+
if container:
75+
return f"{container} {command_name}"
76+
return command_name
77+
78+
79+
def generate_command_frontmatter(command_name: str, command_data: dict, all_commands: dict) -> dict:
80+
"""Generate complete Hugo frontmatter for a command using existing build infrastructure."""
81+
# Get the full command name (handles container commands)
82+
full_command_name = get_full_command_name(command_name, command_data)
83+
84+
# Create Command object to generate syntax using the full command name
85+
c = Command(full_command_name, command_data)
86+
87+
# Start with the command data
88+
fm_data = command_data.copy()
89+
90+
# Add required Hugo frontmatter fields
91+
fm_data.update({
92+
'title': full_command_name,
93+
'linkTitle': full_command_name,
94+
'description': command_data.get('summary'),
95+
'syntax_str': str(c),
96+
'syntax_fmt': c.syntax(),
97+
'hidden': False # Default to not hidden
98+
})
99+
100+
# Add the standard categories from create.sh
101+
add_standard_categories(fm_data)
102+
103+
return fm_data
104+
105+
106+
def generate_argument_sections(command_data: dict) -> str:
107+
"""Generate placeholder sections for Required arguments and Optional arguments."""
108+
content = ""
109+
110+
arguments = command_data.get('arguments', [])
111+
if not arguments:
112+
return content
113+
114+
required_args = []
115+
optional_args = []
116+
117+
# Categorize arguments
118+
for arg in arguments:
119+
if arg.get('optional', False):
120+
optional_args.append(arg)
121+
else:
122+
required_args.append(arg)
123+
124+
# Generate Required arguments section
125+
if required_args:
126+
content += "## Required arguments\n\n"
127+
for arg in required_args:
128+
arg_name = arg.get('name', 'unknown')
129+
arg_type = arg.get('type', 'unknown')
130+
display_text = arg.get('display_text', arg_name)
131+
132+
content += f"<details open><summary><code>{display_text}</code></summary>\n\n"
133+
content += f"TODO: Add description for {arg_name} ({arg_type})\n\n"
134+
content += "</details>\n\n"
135+
136+
# Generate Optional arguments section
137+
if optional_args:
138+
content += "## Optional arguments\n\n"
139+
for arg in optional_args:
140+
arg_name = arg.get('name', 'unknown')
141+
arg_type = arg.get('type', 'unknown')
142+
display_text = arg.get('display_text', arg_name)
143+
token = arg.get('token', '')
144+
145+
content += f"<details open><summary><code>{token if token else display_text}</code></summary>\n\n"
146+
content += f"TODO: Add description for {arg_name} ({arg_type})\n\n"
147+
content += "</details>\n\n"
148+
149+
return content
150+
151+
152+
def generate_return_section() -> str:
153+
"""Generate placeholder Return information section."""
154+
return '''## Return information
155+
156+
{{< multitabs id="return-info"
157+
tab1="RESP2"
158+
tab2="RESP3" >}}
159+
160+
TODO: Add RESP2 return information
161+
162+
-tab-sep-
163+
164+
TODO: Add RESP3 return information
165+
166+
{{< /multitabs >}}
167+
168+
'''
169+
170+
171+
def generate_complete_markdown_content(command_name: str, command_data: dict) -> str:
172+
"""Generate the complete markdown content for a command page."""
173+
content = ""
174+
175+
# Add command summary as the main description
176+
summary = command_data.get('summary', f'TODO: Add summary for {command_name}')
177+
content += f"{summary}\n\n"
178+
179+
# Add argument sections
180+
content += generate_argument_sections(command_data)
181+
182+
# Add return information section
183+
content += generate_return_section()
184+
185+
return content
186+
187+
188+
def create_command_file(command_name: str, command_data: dict, all_commands: dict) -> str:
189+
"""Create a complete command markdown file with frontmatter and content."""
190+
# Get the full command name (handles container commands)
191+
full_command_name = get_full_command_name(command_name, command_data)
192+
193+
# Generate the file path using the full command name
194+
filename = command_filename(full_command_name)
195+
filepath = f'content/commands/{filename}.md'
196+
197+
# Ensure the directory exists
198+
os.makedirs(os.path.dirname(filepath), exist_ok=True)
199+
200+
# Check if file already exists
201+
if os.path.exists(filepath):
202+
logging.warning(f"File {filepath} already exists, skipping...")
203+
return filepath
204+
205+
# Generate frontmatter
206+
frontmatter_data = generate_command_frontmatter(command_name, command_data, all_commands)
207+
208+
# Generate content
209+
content = generate_complete_markdown_content(command_name, command_data)
210+
211+
# Create markdown object and set data
212+
md = Markdown(filepath)
213+
md.fm_data = frontmatter_data
214+
md.payload = content
215+
216+
# Write the file
217+
md.persist()
218+
219+
logging.info(f"Created command file: {filepath}")
220+
return filepath
221+
222+
223+
if __name__ == '__main__':
224+
args = parse_args()
225+
226+
# Configure logging BEFORE creating objects
227+
log_level = getattr(logging, args.loglevel.upper())
228+
logging.basicConfig(
229+
level=log_level,
230+
format='%(message)s %(filename)s:%(lineno)d - %(funcName)s',
231+
force=True # Force reconfiguration in case logging was already configured
232+
)
233+
234+
try:
235+
# Load and validate JSON data
236+
commands_data = load_and_validate_json(args.json_file)
237+
logging.info(f"Loaded {len(commands_data)} commands from {args.json_file}")
238+
239+
# Process each command and generate markdown files
240+
created_files = []
241+
for command_name in commands_data:
242+
try:
243+
logging.info(f"Processing command: {command_name}")
244+
filepath = create_command_file(command_name, commands_data[command_name], commands_data)
245+
created_files.append(filepath)
246+
except Exception as e:
247+
logging.error(f"Failed to create file for command '{command_name}': {e}")
248+
# Continue processing other commands
249+
continue
250+
251+
# Summary
252+
logging.info(f"Successfully created {len(created_files)} command files:")
253+
for filepath in created_files:
254+
logging.info(f" - {filepath}")
255+
256+
except (FileNotFoundError, ValueError) as e:
257+
logging.error(f"Error: {e}")
258+
sys.exit(1)
259+
except Exception as e:
260+
logging.error(f"Unexpected error: {e}")
261+
sys.exit(1)

content/embeds/rs-prometheus-metrics-transition-plan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
| <span class="break-all">bdb_total_req_max</span> | <span class="break-all">`max_over_time(sum by (db) (irate(endpoint_read_requests{db=""$db""}[1m]) + irate(endpoint_write_requests{db=""$db""}[1m]) + irate(endpoint_other_requests{db=""$db""}[1m])) [$__range:])`</span> | Highest value of the rate of all requests on the database (ops/sec) |
6060
| <span class="break-all">bdb_total_res</span> | <span class="break-all">`sum by (db) (irate(endpoint_read_responses{db=""$db""}[1m]) + irate(endpoint_write_responses{db=""$db""}[1m]) + irate(endpoint_other_responses{db=""$db""}[1m]))`</span> | Rate of all responses on the database (ops/sec) |
6161
| <span class="break-all">bdb_total_res_max</span> | <span class="break-all">`max_over_time(sum by (db) (irate(endpoint_read_responses{db=""$db""}[1m]) + irate(endpoint_write_responses{db=""$db""}[1m]) + irate(endpoint_other_responses{db=""$db""}[1m])) [$__range:])`</span> | Highest value of the rate of all responses on the database (ops/sec) |
62-
| <span class="break-all">bdb_up</span> | <span class="break-all">`min by(db) (redis_up)`</span> | Database is up and running |
62+
| <span class="break-all">bdb_up</span> | <span class="break-all">`min by(db) (redis_server_up)`</span> | Database is up and running |
6363
| <span class="break-all">bdb_used_memory</span> | <span class="break-all">`sum by (db) (redis_server_used_memory)`</span> | Memory used by database (in BigRedis this includes flash) (bytes) |
6464
| <span class="break-all">bdb_write_hits</span> | <span class="break-all">`sum by (db) (irate(redis_server_keyspace_write_hits{role="master"}[1m]))`</span> | Rate of write operations accessing an existing key (ops/sec) |
6565
| <span class="break-all">bdb_write_hits_max</span> | <span class="break-all">`sum by (db) (irate(redis_server_keyspace_write_hits{role="master"}[1m]))`</span> | Highest value of the rate of write operations accessing an existing key (ops/sec) |
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
---
2+
Title: Configure modules
3+
alwaysopen: false
4+
categories:
5+
- docs
6+
- operate
7+
- kubernetes
8+
description: Deploy Redis Enterprise databases with modules using the Redis Enterprise operator for Kubernetes.
9+
linkTitle: Configure modules
10+
weight: 15
11+
---
12+
13+
Redis Enterprise modules extend Redis functionality with additional data types, commands, and capabilities. The Redis Enterprise operator supports deploying databases with modules through the `RedisEnterpriseDatabase` (REDB) and `RedisEnterpriseActiveActiveDatabase` (REAADB) custom resources.
14+
15+
## Prerequisites
16+
17+
Before you begin, verify that you have:
18+
19+
- [Redis Enterprise operator deployed]({{< relref "/operate/kubernetes/deployment/quick-start" >}}) in your Kubernetes cluster
20+
- [Redis Enterprise Cluster (REC)]({{< relref "/operate/kubernetes/re-clusters" >}}) running and in a healthy state
21+
- Modules uploaded to the Redis Enterprise cluster (see [Check available modules](#check-available-modules))
22+
23+
## Available modules
24+
25+
Redis Enterprise includes several built-in modules:
26+
27+
| Module | Name | Description |
28+
|--------|------|-------------|
29+
| **[RediSearch]({{< relref "/develop/interact/search-and-query" >}})** | `search` | Full-text search and secondary indexing |
30+
| **[RedisJSON]({{< relref "/develop/data-types/json" >}})** | `ReJSON` | JSON data type support |
31+
| **[RedisTimeSeries]({{< relref "/develop/data-types/timeseries" >}})** | `timeseries` | Time series data structures |
32+
| **[RedisBloom]({{< relref "/develop/data-types/probabilistic" >}})** | `bf` | Probabilistic data structures (Bloom filters, etc.) |
33+
34+
### Check available modules
35+
36+
Before configuring databases with modules, check which modules are available in your cluster:
37+
38+
```bash
39+
kubectl get rec <cluster-name> -o jsonpath='{.status.modules}' | jq
40+
```
41+
42+
This command shows the modules installed in the cluster along with their available versions.
43+
44+
{{< note >}}
45+
Use the `NAME` field instead of the `DISPLAY_NAME` field when configuring databases with modules.
46+
{{< /note >}}
47+
48+
## Install additional modules
49+
50+
If you need to install additional modules or specific versions, upload them using the Redis Enterprise API. See [Upload module v2]({{< relref "/operate/rs/references/rest-api/requests/modules/#post-module-v2" >}}) for more information.
51+
52+
## Module configuration
53+
54+
Each module in the [`modulesList`]({{< relref "/operate/kubernetes/reference/api/redis_enterprise_database_api#specmoduleslist" >}}) supports the following fields:
55+
56+
- **name** (required): The module name (for example, "search", "ReJSON")
57+
- **version** (optional): Specific module version. For Active-Active databases, if specified for one participating cluster, it must be specified for all participating clusters. If omitted, modules will auto-update.
58+
- **config** (optional): Module-specific configuration parameters
59+
60+
For detailed module configuration options and parameters, see [Redis modules]({{< relref "/develop/reference/modules" >}}).
61+
62+
## Upgrade considerations
63+
64+
When upgrading Redis Enterprise clusters or the operator with modules, follow these guidelines:
65+
66+
#### Pre-upgrade planning
67+
68+
- **Check module compatibility**: Verify that your current module versions are compatible with the target Redis Enterprise version. Check each module's [`min_redis_version`](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module/) requirement.
69+
- **Review module dependencies**: Some modules may have specific version requirements or dependencies
70+
- **Document current configurations**: Record all module versions and configurations before upgrading
71+
- **Test in non-production**: Always test module upgrades in a development or staging environment first
72+
73+
#### Module version management during upgrades
74+
75+
- **Upload required modules**: Ensure all necessary module versions are uploaded to the cluster before upgrading
76+
- **Version consistency**: For Active-Active databases, ensure module versions are consistent across all participating clusters. If you specify a version for one cluster, specify the same version for all clusters. Omit versions to allow auto-updates.
77+
- **Compatibility requirements**: Consult the Redis Enterprise documentation for module compatibility matrices and verify each module's [`min_redis_version`](https://redis.io/docs/latest/operate/rs/references/rest-api/objects/module/) requirement
78+
79+
#### Upgrade sequence
80+
81+
1. **Upload new module versions** (if required) to the cluster before upgrading Redis Enterprise
82+
2. **Upgrade the Redis Enterprise cluster** following standard upgrade procedures
83+
3. **Verify module functionality** after the cluster upgrade completes
84+
4. **Update database configurations** if new module versions require configuration changes
85+
86+
#### Post-upgrade verification
87+
88+
- **Check module status**: Verify all modules are loaded correctly: `kubectl get rec <cluster-name> -o jsonpath='{.status.modules}'`
89+
- **Test module functionality**: Validate that module-specific commands and features work as expected
90+
- **Monitor performance**: Watch for any performance changes after the upgrade
91+
- **Update documentation**: Record the new module versions and any configuration changes
92+
93+
For detailed upgrade procedures, see [Upgrade Redis Enterprise clusters]({{< relref "/operate/kubernetes/upgrade/upgrade-redis-cluster" >}}).
94+
95+
## Related information
96+
97+
- [Database controller]({{< relref "/operate/kubernetes/re-databases/db-controller" >}}) - Learn how to create and manage Redis Enterprise databases
98+
- [Active-Active databases]({{< relref "/operate/kubernetes/active-active" >}}) - Set up globally distributed Active-Active databases
99+
- [Database connectivity]({{< relref "/operate/kubernetes/networking/database-connectivity" >}}) - Connect applications to your Redis Enterprise databases
100+
- [REDB API reference]({{< relref "/operate/kubernetes/reference/api/redis_enterprise_database_api" >}}) - Complete API specification for REDB resources
101+
- [REAADB API reference]({{< relref "/operate/kubernetes/reference/api/redis_enterprise_active_active_database_api" >}}) - API reference for Active-Active databases
102+
- [Redis modules documentation](https://redis.io/docs/latest/develop/reference/modules/) - Official Redis modules documentation

0 commit comments

Comments
 (0)