Skip to content

Conversation

@mxsm
Copy link
Owner

@mxsm mxsm commented Jan 8, 2026

Which Issue(s) This PR Fixes(Closes)

Fixes #5555

Brief Description

How Did You Test This Change?

Summary by CodeRabbit

  • Improvements
    • More robust broker startup with multi-step initialization, enhanced logging, ASCII branding, environment verification, and startup info printing.
  • New Features
    • Command-line enhancements: config file selection, print/config inspection flags, and flexible nameserver address handling.
  • Validation
    • Added config and nameserver validation with clearer error reporting and conditional early exit when printing.
  • Tests
    • Unit tests covering nameserver parsing and CLI behaviors.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 2026

Walkthrough

Reworks broker startup into a multi-step initialization: logger/logo, ROCKETMQ_HOME verification, prioritized config file parsing, system-property extraction/application, command-line overrides, validation of broker/namesrv settings, optional config printing, and then broker boot with explicit error handling and exit codes.

Changes

Cohort / File(s) Summary
Command-line Argument Parsing & Validation
rocketmq-broker/src/command.rs
Adds BrokerArgsError and expands Args with config_file, print_config_item, print_important_config, namesrv_addr. Implements validate(), namesrv parsing/validation, helpers (should_exit_after_print, get_namesrv_addr, get_config_file, apply_system_properties), Display impl, CLI metadata, and unit tests for namesrv/arg behaviors.
Bootstrap Server Main Flow
rocketmq-broker/src/bin/broker_bootstrap_server.rs
Replaces single-step startup with multi-step flow: logger and ASCII logo, verify_rocketmq_home, parse_config_file(args) (priority: CLI -c, defaults), extract_properties_from_config, apply_system_properties, apply_command_line_args, validate_broker_config / validate_namesrv_address, config printing helpers (print_config, print_important_*, print_all_*), print_startup_info, early exit on print flags, and integrated error logging; adds tests for namesrv validation.

Sequence Diagram(s)

sequenceDiagram
    %% Participants
    participant User
    participant CLI as CLI Parser (Args)
    participant Env as Environment (ROCKETMQ_HOME)
    participant FS as Filesystem/Config
    participant Props as System Properties
    participant Validator as Validator
    participant Broker as Broker Boot

    User->>CLI: invoke broker with args
    CLI->>CLI: parse args
    CLI->>Env: verify ROCKETMQ_HOME
    alt ROCKETMQ_HOME invalid
        Env-->>CLI: error
        CLI-->>User: exit with failure
    else ROCKETMQ_HOME valid
        CLI->>FS: locate & parse config file (priority: -c then default)
        FS-->>CLI: BrokerConfig + MessageStoreConfig
        CLI->>Props: extract_properties_from_config
        Props-->>CLI: properties map
        CLI->>CLI: apply_system_properties & apply_command_line_args
        CLI->>Validator: validate_namesrv_address & validate_broker_config
        alt validation fails
            Validator-->>CLI: error
            CLI-->>User: exit with failure
        else validation succeeds
            alt print requested
                CLI->>User: print config (important/all) and exit
            else
                CLI->>CLI: print_startup_info
                CLI->>Broker: boot broker with final configs
                Broker-->>User: broker running
            end
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped in with args, a config in my paw,

I checked the home, then parsed with awe,
Properties sprinkled, validations done,
I printed the bits, then off to run—
A startup carrot for everyone! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: enhanced broker startup with improved argument validation and configuration handling across two files.
Linked Issues check ✅ Passed The PR implements improvements to broker startup argument validation and configuration handling as indicated by issue #5555, though the issue lacks detailed requirements.
Out of Scope Changes check ✅ Passed All changes focus on broker startup enhancements, argument validation, and configuration handling; no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rocketmq-rust-bot
Copy link
Collaborator

🔊@mxsm 🚀Thanks for your contribution🎉!

💡CodeRabbit(AI) will review your code first🔥!

Note

🚨The code review suggestions from CodeRabbit are to be used as a reference only, and the PR submitter can decide whether to make changes based on their own judgment. Ultimately, the project management personnel will conduct the final code review💥.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @rocketmq-broker/src/bin/broker_bootstrap_server.rs:
- Around line 173-189: The branch incorrectly treats an existing-but-empty
NAMESRV_ADDR env var as a valid override because it uses
env::var("NAMESRV_ADDR").is_ok(); change the condition so it only triggers when
the env var is non-empty (e.g. replace env::var(...).is_ok() with
env::var("NAMESRV_ADDR").ok().filter(|s| !s.is_empty()).is_some()), keep the
existing args.namesrv_addr check and continue using args.get_namesrv_addr() to
populate broker_config.namesrv_addr and logging via
broker_config.namesrv_addr.as_ref().unwrap().

In @rocketmq-broker/src/command.rs:
- Around line 256-271: The four calls to env::set_var inside
apply_system_properties need to be wrapped in unsafe blocks because env::set_var
is an unsafe fn in Rust 1.85.0; update each call (both the domain and subgroup
branches where env::set_var("rocketmq.namesrv.domain", ...),
env::set_var("rocketmq.namesrv.domain", DEFAULT_DOMAIN),
env::set_var("rocketmq.namesrv.domain.subgroup", ...), and
env::set_var("rocketmq.namesrv.domain.subgroup", DEFAULT_SUBGROUP)) to be
invoked as unsafe { env::set_var(...) } so the function compiles under the
declared MSRV while preserving the existing logic and single-threaded
initialization assumption.
🧹 Nitpick comments (5)
rocketmq-broker/src/command.rs (1)

125-143: Consider separating logging from pure validation.

The validate() method performs logging (info!) as a side effect. This can be unexpected behavior for a validation function, which is typically expected to be pure. Logging could be moved to the caller after successful validation.

rocketmq-broker/src/bin/broker_bootstrap_server.rs (4)

59-98: Consider returning errors instead of using process::exit.

Using process::exit() bypasses Rust's normal cleanup mechanisms (destructors won't run). Since main() already returns Result<()>, consider propagating errors instead. This would be more idiomatic and allow proper resource cleanup.

♻️ Proposed refactor sketch
-    if let Err(e) = args.validate() {
-        error!("Invalid arguments: {}", e);
-        process::exit(-1);
-    }
+    args.validate().map_err(|e| {
+        error!("Invalid arguments: {}", e);
+        anyhow::anyhow!("Invalid arguments: {}", e)
+    })?;

Apply similar pattern to other process::exit calls, using anyhow::bail! or ? operator to propagate errors.


155-165: Placeholder implementation - consider marking as TODO or implementing.

This function currently returns an empty HashMap, making apply_system_properties always use default values. If this is intentional for now, consider adding a TODO comment or removing the call until it's properly implemented.


211-234: Duplicate validation logic with different strictness.

validate_namesrv_address here only checks for a colon separator, while Args::validate_namesrv_addr in command.rs performs stricter validation (SocketAddr parsing, port number validation). This inconsistency could allow invalid addresses through if they bypass CLI validation. Consider reusing the stricter validation or consolidating the logic.


284-294: Incomplete implementation for MessageStoreConfig printing.

The comment notes that MessageStoreConfig doesn't implement get_properties yet. This results in the "all config" output being less complete than "important config" for message store settings. Consider adding a TODO or implementing get_properties for consistency.

Would you like me to help create an issue to track implementing get_properties for MessageStoreConfig?

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 44bbd1c and 82543da.

📒 Files selected for processing (2)
  • rocketmq-broker/src/bin/broker_bootstrap_server.rs
  • rocketmq-broker/src/command.rs
🧰 Additional context used
🧬 Code graph analysis (1)
rocketmq-broker/src/command.rs (1)
rocketmq-common/src/common/namesrv/namesrv_config.rs (1)
  • rocketmq_home (31-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Check (fmt + clippy)
  • GitHub Check: auto-approve
🔇 Additional comments (8)
rocketmq-broker/src/command.rs (4)

25-51: LGTM! Well-structured error enum.

The BrokerArgsError enum provides clear, descriptive error messages with proper use of thiserror. The error variants cover the expected failure modes for argument validation.


60-112: LGTM! Clean clap configuration with appropriate constraints.

The conflicts_with attribute correctly ensures mutual exclusivity between -p and -m flags. The documentation clearly explains the precedence and format expectations.


205-223: LGTM! Clear priority chain for nameserver address resolution.

The priority (CLI → environment → default) is well-documented and correctly implemented.


274-293: LGTM!

The Display implementation provides a clean, readable format for debugging and logging purposes.

rocketmq-broker/src/bin/broker_bootstrap_server.rs (4)

123-126: ROCKETMQ_HOME validation inconsistency.

If ROCKETMQ_HOME is set but points to a non-existent or non-directory path, only a warning is logged and startup continues. This could lead to confusing errors later (e.g., when trying to load config from $ROCKETMQ_HOME/conf/broker.toml). Consider failing early or documenting this as intentional behavior.


138-153: LGTM! Config parsing with appropriate error context.

The use of with_context provides helpful error messages when config parsing fails. The fallback to defaults when no config file exists is sensible.


296-313: LGTM!

Clear and informative startup logging that helps with debugging and operational visibility.


315-338: LGTM!

Unit tests provide good coverage of the nameserver address validation edge cases.

Comment on lines +173 to +189
if args.namesrv_addr.is_some() || env::var("NAMESRV_ADDR").is_ok() {
let namesrv_addr = args.get_namesrv_addr();
broker_config.namesrv_addr = Some(namesrv_addr.into());
info!(
"Name server address (from command line/env): {}",
broker_config.namesrv_addr.as_ref().unwrap()
);
} else if let Some(ref addr) = broker_config.namesrv_addr {
info!("Name server address (from config file): {}", addr);
} else {
// Use default if not set anywhere
broker_config.namesrv_addr = Some("127.0.0.1:9876".to_string().into());
info!(
"Name server address (default): {}",
broker_config.namesrv_addr.as_ref().unwrap()
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Empty NAMESRV_ADDR env var triggers override path unexpectedly.

env::var("NAMESRV_ADDR").is_ok() returns true if the variable exists but is empty. This causes the code to enter the CLI/env override branch, though get_namesrv_addr() would then return the default. Consider checking for non-empty explicitly to match the behavior in get_namesrv_addr().

🔧 Proposed fix
-    if args.namesrv_addr.is_some() || env::var("NAMESRV_ADDR").is_ok() {
+    if args.namesrv_addr.is_some() || env::var("NAMESRV_ADDR").map(|v| !v.is_empty()).unwrap_or(false) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if args.namesrv_addr.is_some() || env::var("NAMESRV_ADDR").is_ok() {
let namesrv_addr = args.get_namesrv_addr();
broker_config.namesrv_addr = Some(namesrv_addr.into());
info!(
"Name server address (from command line/env): {}",
broker_config.namesrv_addr.as_ref().unwrap()
);
} else if let Some(ref addr) = broker_config.namesrv_addr {
info!("Name server address (from config file): {}", addr);
} else {
// Use default if not set anywhere
broker_config.namesrv_addr = Some("127.0.0.1:9876".to_string().into());
info!(
"Name server address (default): {}",
broker_config.namesrv_addr.as_ref().unwrap()
);
}
if args.namesrv_addr.is_some() || env::var("NAMESRV_ADDR").map(|v| !v.is_empty()).unwrap_or(false) {
let namesrv_addr = args.get_namesrv_addr();
broker_config.namesrv_addr = Some(namesrv_addr.into());
info!(
"Name server address (from command line/env): {}",
broker_config.namesrv_addr.as_ref().unwrap()
);
} else if let Some(ref addr) = broker_config.namesrv_addr {
info!("Name server address (from config file): {}", addr);
} else {
// Use default if not set anywhere
broker_config.namesrv_addr = Some("127.0.0.1:9876".to_string().into());
info!(
"Name server address (default): {}",
broker_config.namesrv_addr.as_ref().unwrap()
);
}
🤖 Prompt for AI Agents
In @rocketmq-broker/src/bin/broker_bootstrap_server.rs around lines 173 - 189,
The branch incorrectly treats an existing-but-empty NAMESRV_ADDR env var as a
valid override because it uses env::var("NAMESRV_ADDR").is_ok(); change the
condition so it only triggers when the env var is non-empty (e.g. replace
env::var(...).is_ok() with env::var("NAMESRV_ADDR").ok().filter(|s|
!s.is_empty()).is_some()), keep the existing args.namesrv_addr check and
continue using args.get_namesrv_addr() to populate broker_config.namesrv_addr
and logging via broker_config.namesrv_addr.as_ref().unwrap().

Comment on lines +186 to +195
/// Check if a string is valid host:port format
fn is_valid_host_port(&self, addr: &str) -> bool {
let parts: Vec<&str> = addr.split(':').collect();
if parts.len() != 2 {
return false;
}

// Validate port is a number
parts[1].parse::<u16>().is_ok()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Host validation is insufficient.

is_valid_host_port only validates that the port is a valid u16 but doesn't validate the host portion. Addresses like :9876, !@#$:9876, or empty hosts would pass validation.

🔧 Proposed fix to add minimal host validation
 fn is_valid_host_port(&self, addr: &str) -> bool {
     let parts: Vec<&str> = addr.split(':').collect();
     if parts.len() != 2 {
         return false;
     }

+    // Validate host is not empty
+    let host = parts[0].trim();
+    if host.is_empty() {
+        return false;
+    }
+
     // Validate port is a number
     parts[1].parse::<u16>().is_ok()
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Check if a string is valid host:port format
fn is_valid_host_port(&self, addr: &str) -> bool {
let parts: Vec<&str> = addr.split(':').collect();
if parts.len() != 2 {
return false;
}
// Validate port is a number
parts[1].parse::<u16>().is_ok()
}
/// Check if a string is valid host:port format
fn is_valid_host_port(&self, addr: &str) -> bool {
let parts: Vec<&str> = addr.split(':').collect();
if parts.len() != 2 {
return false;
}
// Validate host is not empty
let host = parts[0].trim();
if host.is_empty() {
return false;
}
// Validate port is a number
parts[1].parse::<u16>().is_ok()
}

Comment on lines +256 to +271
pub fn apply_system_properties(properties: &HashMap<String, String>) {
const DEFAULT_DOMAIN: &str = "jmenv.tbsite.net";
const DEFAULT_SUBGROUP: &str = "nsaddr";

if let Some(domain) = properties.get("rmqAddressServerDomain") {
env::set_var("rocketmq.namesrv.domain", domain);
} else {
env::set_var("rocketmq.namesrv.domain", DEFAULT_DOMAIN);
}

if let Some(subgroup) = properties.get("rmqAddressServerSubGroup") {
env::set_var("rocketmq.namesrv.domain.subgroup", subgroup);
} else {
env::set_var("rocketmq.namesrv.domain.subgroup", DEFAULT_SUBGROUP);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

env::set_var thread safety Rust 1.80 unsafe deprecated

💡 Result:

Short answer: In modern Rust (including 1.80) std::env::set_var is marked unsafe because it is not safe to call in general multi-threaded programs on Unix-like platforms (Windows is safe). You must call it inside an unsafe block and ensure no other threads may read or write the process environment; prefer Command::env for child processes. [1][2][3]

Notes/sources:

  • std::env::set_var docs (safety text: safe only single‑threaded except on Windows). [1]
  • Rust 2024/edition guidance: set_var/remove_var were made unsafe (migration guidance). [2]
  • Historical discussion about deprecating/changing set_var (context on thread-safety concerns). [3]

🌐 Web query:

Rust std::env::set_var safety documentation changelog

💡 Result:

Short summary

  • std::env::set_var was made unsafe in the Rust 2024 edition; the standard-library docs now show its signature as pub unsafe fn set_var(...) and include the safety text about single-threaded/Windows-only safety (Unix multi-threaded use is unsound). [1]

  • The change is documented in the Rust 2024 edition guide under "Newly unsafe functions" (lists std::env::set_var and std::env::remove_var). [2]

  • The Rust release announcement / changelog for the stable release that stabilizes the 2024 edition also lists these functions as newly unsafe. [3]

  • Related compiler support: the deprecated-safe-2024 lint flags uses of functions that were safe before but are unsafe in Rust 2024. [4]

Sources
[1] std::env::set_var docs.
[2] Rust Edition Guide — Newly unsafe functions.
[3] Rust blog release notes (Rust 1.85.0 / Rust 2024 announcement).
[4] DEPRECATED_SAFE_2024 lint documentation.


🏁 Script executed:

cd rocketmq-broker && cat -n src/command.rs | sed -n '250,275p'

Repository: mxsm/rocketmq-rust

Length of output: 1264


🏁 Script executed:

cd rocketmq-broker && cat Cargo.toml | grep -A5 "edition\|rust-version"

Repository: mxsm/rocketmq-rust

Length of output: 431


🏁 Script executed:

cd rocketmq-broker && rg "apply_system_properties" -B5 -A15

Repository: mxsm/rocketmq-rust

Length of output: 2768


🏁 Script executed:

cat -n Cargo.toml | head -30

Repository: mxsm/rocketmq-rust

Length of output: 907


🏁 Script executed:

rg "edition|rust-version" Cargo.toml | head -20

Repository: mxsm/rocketmq-rust

Length of output: 102


Wrap env::set_var calls in unsafe blocks — required by Rust 1.85.0+.

env::set_var was made unsafe fn in Rust 1.85.0 (Rust 2024 edition) because it is unsound in multi-threaded contexts on Unix. Your declared MSRV is 1.85.0, so the four env::set_var calls on lines 261, 263, 267, and 269 must each be wrapped in unsafe { } blocks to compile. While this code runs only once during single-threaded initialization, the unsafe block is now mandatory and documents the single-threaded assumption.

🤖 Prompt for AI Agents
In @rocketmq-broker/src/command.rs around lines 256 - 271, The four calls to
env::set_var inside apply_system_properties need to be wrapped in unsafe blocks
because env::set_var is an unsafe fn in Rust 1.85.0; update each call (both the
domain and subgroup branches where env::set_var("rocketmq.namesrv.domain", ...),
env::set_var("rocketmq.namesrv.domain", DEFAULT_DOMAIN),
env::set_var("rocketmq.namesrv.domain.subgroup", ...), and
env::set_var("rocketmq.namesrv.domain.subgroup", DEFAULT_SUBGROUP)) to be
invoked as unsafe { env::set_var(...) } so the function compiles under the
declared MSRV while preserving the existing logic and single-threaded
initialization assumption.

Comment on lines +362 to +373
#[test]
fn test_get_namesrv_addr_default() {
let args = Args {
config_file: None,
print_config_item: false,
print_important_config: false,
namesrv_addr: None,
};

// Should return default if no env var set
assert_eq!(args.get_namesrv_addr(), "127.0.0.1:9876");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Test may be flaky if NAMESRV_ADDR environment variable is set.

test_get_namesrv_addr_default assumes no NAMESRV_ADDR environment variable is set, but this may not hold in all test environments. Consider explicitly unsetting the variable before the assertion to ensure test isolation.

🔧 Proposed fix
 #[test]
 fn test_get_namesrv_addr_default() {
+    // Ensure env var is not set for this test
+    std::env::remove_var("NAMESRV_ADDR");
+
     let args = Args {
         config_file: None,
         print_config_item: false,
         print_important_config: false,
         namesrv_addr: None,
     };

     // Should return default if no env var set
     assert_eq!(args.get_namesrv_addr(), "127.0.0.1:9876");
 }

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @rocketmq-broker/src/bin/broker_bootstrap_server.rs:
- Around line 114-130: The is_empty() check in verify_rocketmq_home() is
unreachable because EnvUtils::get_rocketmq_home() always returns a non-empty
fallback; update verify_rocketmq_home to first check the actual environment
variable (e.g., std::env::var or var_os for "ROCKETMQ_HOME") and bail if it is
not set or empty, then call EnvUtils::get_rocketmq_home() to construct the
PathBuf and perform the exists()/is_dir() checks; remove the redundant
home.is_empty() branch so the function enforces an explicit ROCKETMQ_HOME while
preserving the directory validation and logging.
🧹 Nitpick comments (3)
rocketmq-broker/src/bin/broker_bootstrap_server.rs (3)

155-162: Placeholder function returns empty map.

This stub always returns an empty HashMap, so Args::apply_system_properties() will only apply defaults. This is acceptable if the current typed struct approach is intentional, but consider adding a TODO or removing the function if it's not expected to be enhanced.


208-231: Basic address validation only checks for colon presence.

The validation catches malformed addresses without ports but doesn't validate that the port is numeric or within a valid range (1-65535). This is acceptable for a basic check, but consider enhancing if stricter validation is needed.

♻️ Optional enhancement for stricter validation
         // Basic validation: should contain colon for port
         if !trimmed.contains(':') {
             anyhow::bail!(
                 "Invalid name server address format: {}. Expected format: '127.0.0.1:9876;192.168.0.1:9876'",
                 namesrv_addr
             );
         }
+
+        // Validate port is numeric and in valid range
+        if let Some(port_str) = trimmed.rsplit(':').next() {
+            match port_str.parse::<u16>() {
+                Ok(0) => anyhow::bail!("Invalid port 0 in address: {}", trimmed),
+                Err(_) => anyhow::bail!("Invalid port '{}' in address: {}", port_str, trimmed),
+                _ => {}
+            }
+        }

312-335: Good test coverage for basic cases.

The tests cover single address, multiple addresses, invalid format, and empty input. Consider adding edge case tests for addresses with empty segments (e.g., "127.0.0.1:9876;;192.168.0.2:9876") and whitespace-only segments to verify the trim() and continue logic.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82543da and 495879a.

📒 Files selected for processing (2)
  • rocketmq-broker/src/bin/broker_bootstrap_server.rs
  • rocketmq-broker/src/command.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • rocketmq-broker/src/command.rs
🧰 Additional context used
🧬 Code graph analysis (1)
rocketmq-broker/src/bin/broker_bootstrap_server.rs (4)
rocketmq-common/src/utils/parse_config_file.rs (2)
  • parse_config_file (23-32)
  • cfg (30-30)
rocketmq-broker/src/command.rs (1)
  • apply_system_properties (256-271)
rocketmq-common/src/utils/env_utils.rs (1)
  • get_rocketmq_home (120-132)
rocketmq-common/src/common/broker/broker_config.rs (2)
  • broker_identity (59-61)
  • broker_cluster_name (50-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build & Test (macos-latest)
  • GitHub Check: Build & Test (windows-latest)
  • GitHub Check: Build & Test (ubuntu-latest)
  • GitHub Check: Code Coverage
  • GitHub Check: auto-approve
🔇 Additional comments (4)
rocketmq-broker/src/bin/broker_bootstrap_server.rs (4)

45-112: Well-structured startup flow with clear error handling.

The main function demonstrates good practices: distinct exit codes for different failure modes, clear sequential initialization steps, and proper separation of concerns. The workflow (logger → home verification → config parsing → property extraction → CLI override → validation → optional print → boot) is logical and maintainable.


189-206: Validation is minimal but correct for the current scope.

The function currently only validates the name server address format. The docstring mentions "Check broker role configuration" and "Validate broker ID" which aren't fully implemented yet. Consider either implementing these checks or updating the docstring to match the current implementation.


272-291: Asymmetric implementation between config printing functions.

print_all_broker_config uses get_properties() for dynamic enumeration while print_all_message_store_config manually lists fields. The comment at line 284 acknowledges this limitation. This is acceptable, but consider implementing get_properties() for MessageStoreConfig for consistency if this pattern is used elsewhere.


293-310: Good startup logging for operational visibility.

The function provides useful structured logging for broker identity, name server address, and listen endpoint, which is helpful for debugging and monitoring.

Comment on lines +114 to +130
/// Verify ROCKETMQ_HOME environment variable is set
fn verify_rocketmq_home() -> Result<()> {
let home = EnvUtils::get_rocketmq_home();
info!("Rocketmq(Rust) home: {}", home);
let config = if let Some(ref config_file) = args.config_file {
let config_file = PathBuf::from(config_file);
info!("Using config file: {:?}", config_file);
if !config_file.exists() || !config_file.is_file() {
bail!("Config file does not exist or is not a file: {:?}", config_file);
}
Ok((
ParseConfigFile::parse_config_file::<BrokerConfig>(config_file.clone())?,
ParseConfigFile::parse_config_file::<MessageStoreConfig>(config_file)?,
))
if home.is_empty() {
anyhow::bail!(
"Please set the ROCKETMQ_HOME environment variable to match the location of the RocketMQ installation"
);
}

let home_path = PathBuf::from(&home);
if !home_path.exists() || !home_path.is_dir() {
warn!("ROCKETMQ_HOME directory does not exist or is not a directory: {}", home);
}

info!("ROCKETMQ_HOME: {}", home);
Ok(())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

home.is_empty() check is unreachable due to get_rocketmq_home() fallback behavior.

Based on the EnvUtils::get_rocketmq_home() implementation in rocketmq-common/src/utils/env_utils.rs, the function always returns a non-empty string (falling back to "." if ROCKETMQ_HOME is not set). The is_empty() check on line 117 will never be true.

If the intent is to require explicit ROCKETMQ_HOME configuration, consider checking the environment variable directly before calling get_rocketmq_home().

🔧 Proposed fix
 fn verify_rocketmq_home() -> Result<()> {
+    // Check if ROCKETMQ_HOME is explicitly set before using get_rocketmq_home()
+    // which has fallback behavior
+    if env::var("ROCKETMQ_HOME").map(|v| v.is_empty()).unwrap_or(true) {
+        warn!("ROCKETMQ_HOME is not set, using current directory as default");
+    }
+
     let home = EnvUtils::get_rocketmq_home();
-    if home.is_empty() {
-        anyhow::bail!(
-            "Please set the ROCKETMQ_HOME environment variable to match the location of the RocketMQ installation"
-        );
-    }
 
     let home_path = PathBuf::from(&home);
     if !home_path.exists() || !home_path.is_dir() {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Verify ROCKETMQ_HOME environment variable is set
fn verify_rocketmq_home() -> Result<()> {
let home = EnvUtils::get_rocketmq_home();
info!("Rocketmq(Rust) home: {}", home);
let config = if let Some(ref config_file) = args.config_file {
let config_file = PathBuf::from(config_file);
info!("Using config file: {:?}", config_file);
if !config_file.exists() || !config_file.is_file() {
bail!("Config file does not exist or is not a file: {:?}", config_file);
}
Ok((
ParseConfigFile::parse_config_file::<BrokerConfig>(config_file.clone())?,
ParseConfigFile::parse_config_file::<MessageStoreConfig>(config_file)?,
))
if home.is_empty() {
anyhow::bail!(
"Please set the ROCKETMQ_HOME environment variable to match the location of the RocketMQ installation"
);
}
let home_path = PathBuf::from(&home);
if !home_path.exists() || !home_path.is_dir() {
warn!("ROCKETMQ_HOME directory does not exist or is not a directory: {}", home);
}
info!("ROCKETMQ_HOME: {}", home);
Ok(())
}
/// Verify ROCKETMQ_HOME environment variable is set
fn verify_rocketmq_home() -> Result<()> {
// Check if ROCKETMQ_HOME is explicitly set before using get_rocketmq_home()
// which has fallback behavior
if env::var("ROCKETMQ_HOME").map(|v| v.is_empty()).unwrap_or(true) {
warn!("ROCKETMQ_HOME is not set, using current directory as default");
}
let home = EnvUtils::get_rocketmq_home();
let home_path = PathBuf::from(&home);
if !home_path.exists() || !home_path.is_dir() {
warn!("ROCKETMQ_HOME directory does not exist or is not a directory: {}", home);
}
info!("ROCKETMQ_HOME: {}", home);
Ok(())
}
🤖 Prompt for AI Agents
In @rocketmq-broker/src/bin/broker_bootstrap_server.rs around lines 114 - 130,
The is_empty() check in verify_rocketmq_home() is unreachable because
EnvUtils::get_rocketmq_home() always returns a non-empty fallback; update
verify_rocketmq_home to first check the actual environment variable (e.g.,
std::env::var or var_os for "ROCKETMQ_HOME") and bail if it is not set or empty,
then call EnvUtils::get_rocketmq_home() to construct the PathBuf and perform the
exists()/is_dir() checks; remove the redundant home.is_empty() branch so the
function enforces an explicit ROCKETMQ_HOME while preserving the directory
validation and logging.

Comment on lines +132 to +153
/// Parse configuration from file
///
/// Priority:
/// 1. Explicit config file from `-c` argument
/// 2. $ROCKETMQ_HOME/conf/broker.toml
/// 3. Default configuration
fn parse_config_file(args: &Args) -> Result<(BrokerConfig, MessageStoreConfig)> {
if let Some(config_file) = args.get_config_file() {
info!("Loading configuration from: {}", config_file.display());

let broker_config = ParseConfigFile::parse_config_file::<BrokerConfig>(config_file.clone())
.with_context(|| format!("Failed to parse BrokerConfig from {:?}", config_file))?;

let message_store_config = ParseConfigFile::parse_config_file::<MessageStoreConfig>(config_file.clone())
.with_context(|| format!("Failed to parse MessageStoreConfig from {:?}", config_file))?;

Ok((broker_config, message_store_config))
} else {
let config_file = PathBuf::from(home.as_str()).join("conf").join("broker.toml");
info!("Using config file: {:?}", config_file);
if !config_file.exists() || !config_file.is_file() {
return Ok((Default::default(), Default::default()));
info!("Using default configuration (no config file specified)");
Ok((BrokerConfig::default(), MessageStoreConfig::default()))
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Implementation missing documented fallback to $ROCKETMQ_HOME/conf/broker.toml.

The docstring states priority 2 is $ROCKETMQ_HOME/conf/broker.toml, but the implementation only handles the explicit -c argument (priority 1) and default configuration (priority 3). The intermediate fallback path is not implemented.

🔧 Proposed fix to implement the documented behavior
 fn parse_config_file(args: &Args) -> Result<(BrokerConfig, MessageStoreConfig)> {
     if let Some(config_file) = args.get_config_file() {
         info!("Loading configuration from: {}", config_file.display());
 
         let broker_config = ParseConfigFile::parse_config_file::<BrokerConfig>(config_file.clone())
             .with_context(|| format!("Failed to parse BrokerConfig from {:?}", config_file))?;
 
         let message_store_config = ParseConfigFile::parse_config_file::<MessageStoreConfig>(config_file.clone())
             .with_context(|| format!("Failed to parse MessageStoreConfig from {:?}", config_file))?;
 
         Ok((broker_config, message_store_config))
     } else {
+        // Try default config file location
+        let default_config = PathBuf::from(EnvUtils::get_rocketmq_home())
+            .join("conf")
+            .join("broker.toml");
+
+        if default_config.exists() {
+            info!("Loading configuration from default location: {}", default_config.display());
+
+            let broker_config = ParseConfigFile::parse_config_file::<BrokerConfig>(default_config.clone())
+                .with_context(|| format!("Failed to parse BrokerConfig from {:?}", default_config))?;
+
+            let message_store_config = ParseConfigFile::parse_config_file::<MessageStoreConfig>(default_config.clone())
+                .with_context(|| format!("Failed to parse MessageStoreConfig from {:?}", default_config))?;
+
+            return Ok((broker_config, message_store_config));
+        }
+
         info!("Using default configuration (no config file specified)");
         Ok((BrokerConfig::default(), MessageStoreConfig::default()))
     }
 }

Committable suggestion skipped: line range outside the PR's diff.

@codecov
Copy link

codecov bot commented Jan 8, 2026

Codecov Report

❌ Patch coverage is 42.90123% with 185 lines in your changes missing coverage. Please review.
✅ Project coverage is 38.54%. Comparing base (44bbd1c) to head (495879a).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
rocketmq-broker/src/bin/broker_bootstrap_server.rs 16.89% 123 Missing ⚠️
rocketmq-broker/src/command.rs 64.77% 62 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5556      +/-   ##
==========================================
+ Coverage   38.49%   38.54%   +0.04%     
==========================================
  Files         815      816       +1     
  Lines      110608   110917     +309     
==========================================
+ Hits        42575    42748     +173     
- Misses      68033    68169     +136     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Collaborator

@rocketmq-rust-bot rocketmq-rust-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - All CI checks passed ✅

@rocketmq-rust-bot rocketmq-rust-bot merged commit 6642235 into main Jan 8, 2026
13 of 14 checks passed
@rocketmq-rust-bot rocketmq-rust-bot added approved PR has approved and removed ready to review waiting-review waiting review this PR labels Jan 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhancement✨] Enhance broker startup process with improved argument validation and configuration handling

4 participants