Skip to content

Add --runtime_info_file flag for ephemeral port discovery#5013

Merged
pwojcikdev merged 3 commits intonanocurrency:developfrom
pwojcikdev:system-test-port-conflicts
Jan 27, 2026
Merged

Add --runtime_info_file flag for ephemeral port discovery#5013
pwojcikdev merged 3 commits intonanocurrency:developfrom
pwojcikdev:system-test-port-conflicts

Conversation

@pwojcikdev
Copy link
Copy Markdown
Contributor

New CLI Options

--runtime_info_file [path]

Writes a JSON file containing runtime information after the node starts. This is particularly useful when running with ephemeral ports (peering_port=0, rpc.port=0) where the actual bound ports are assigned by the OS.

Usage:

nano_node --daemon --runtime_info_file                     # Uses default: runtime_info.json                                                                                                                                                                                                                                                                                                                                            
nano_node --daemon --runtime_info_file /path/to/info.json  # Custom path                                                                                                                                                                                                                                                                                                                                                                

JSON output:

{                                                                                                                                                                                                                                                                                                                                                                                                                                       
  "peering_port": 54321,                                                                                                                                                                                                                                                                                                                                                                                                                
  "rpc_port": 54322,                                                                                                                                                                                                                                                                                                                                                                                                                    
  "node_id": "node_1abc..."                                                                                                                                                                                                                                                                                                                                                                                                             
}

Notes:

  • rpc_port is only included when RPC is enabled
  • File is automatically deleted on clean process exit (SIGINT/SIGTERM or RPC stop)

--pid_file [path] (improved)

Now uses the same automatic cleanup infrastructure. File is automatically deleted on process exit.

nano_node --daemon --pid_file                    # Uses default: nano_node.pid                                                                                                                                                                                                                                                                                                                                                          
nano_node --daemon --pid_file /path/to/node.pid  # Custom path

This should fix system test flakiness.

@pwojcikdev pwojcikdev added documentation This item indicates the need for or supplies updated or expanded documentation cli Changes related to the Command Line Interface labels Jan 26, 2026
@gr0vity-dev-bot
Copy link
Copy Markdown

gr0vity-dev-bot commented Jan 26, 2026

Test Results for Commit 1fe6305

Pull Request 5013: Results
Overall Status:

Test Case Results

  • 5n4pr_conf_10k_bintree: PASS (Duration: 110s)
  • 5n4pr_conf_10k_change: PASS (Duration: 123s)
  • 5n4pr_conf_change_dependant: PASS (Duration: 140s)
  • 5n4pr_conf_change_independant: PASS (Duration: 133s)
  • 5n4pr_conf_send_dependant: PASS (Duration: 131s)
  • 5n4pr_conf_send_independant: PASS (Duration: 114s)
  • 5n4pr_rocks_10k_bintree: PASS (Duration: 109s)
  • 5n4pr_rocks_10k_change: FAIL (Duration: 271s)
  • Log

Last updated: 2026-01-27 04:32:06 UTC

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request adds support for discovering ephemeral ports through a new --runtime_info_file flag and improves the existing --pid_file flag by implementing automatic cleanup on process exit.

Changes:

  • Introduces a runtime file management system that registers files for automatic cleanup on process exit using std::atexit()
  • Adds --runtime_info_file CLI flag that writes a JSON file containing the node's actual bound ports (peering_port, rpc_port) and node_id, particularly useful when using ephemeral ports (port=0)
  • Updates system tests to use ephemeral ports and the new runtime_info_file for discovering actual bound ports, eliminating hardcoded ports that caused test flakiness

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
nano/lib/runtime_files.hpp New header defining runtime file management API for automatic cleanup of PID and runtime info files
nano/lib/runtime_files.cpp Implementation of runtime file management with thread-safe registration and atexit-based cleanup
nano/lib/CMakeLists.txt Added new runtime_files source files to build
nano/node/nodeconfig.hpp Added runtime_info_file optional field to node_flags
nano/node/cli.cpp Added --runtime_info_file CLI option with implicit value support
nano/node/network.cpp Minor style change to use brace initialization for endpoint
nano/nano_node/daemon.hpp Updated parameter name for clarity
nano/nano_node/daemon.cpp Integrated runtime_info_file creation after node and RPC startup
nano/nano_node/entry.cpp Refactored PID file handling to use new runtime_files infrastructure
systest/runtime_files.sh New comprehensive test for PID file and runtime info file creation and cleanup
systest/rpc_stop.sh Updated to use ephemeral ports and runtime_info_file for port discovery
systest/daemon_interrupt.sh Updated to use ephemeral ports and runtime_info_file for readiness detection
systest/bind_failure.sh Updated to use ephemeral ports and runtime_info_file instead of hardcoded ports

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +168 to +169
nano::runtime_files::create_pid_file (pid_filepath);
std::cerr << "PID file created: " << pid_filepath << std::endl;
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

Similar to the runtime_info_file handling in daemon.cpp, if create_pid_file() throws an exception, there's no specific error handling. The exception will propagate and may not provide a clear error message to the user about what went wrong with PID file creation. Consider adding a try-catch block to provide a more informative error message.

Suggested change
nano::runtime_files::create_pid_file (pid_filepath);
std::cerr << "PID file created: " << pid_filepath << std::endl;
try
{
nano::runtime_files::create_pid_file (pid_filepath);
std::cerr << "PID file created: " << pid_filepath << std::endl;
}
catch (std::exception const & e)
{
std::cerr << "Failed to create PID file '" << pid_filepath << "': " << e.what () << std::endl;
return 1;
}

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +67
{
std::lock_guard<std::mutex> lock (mutex);
registered_files.erase (path);
}

std::error_code ec;
std::filesystem::remove (path, ec);
// Ignore errors - file may not exist
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The lock is released before the file is removed from the filesystem at line 66. This means another thread could re-insert the same path into registered_files between the erase and the actual file removal. While this is unlikely to cause issues in practice (since the file removal is best-effort anyway), it creates a small window where the state is inconsistent. Consider moving the filesystem removal inside the locked section, or document this behavior if it's intentional for performance reasons.

Suggested change
{
std::lock_guard<std::mutex> lock (mutex);
registered_files.erase (path);
}
std::error_code ec;
std::filesystem::remove (path, ec);
// Ignore errors - file may not exist
std::error_code ec;
{
std::lock_guard<std::mutex> lock (mutex);
registered_files.erase (path);
std::filesystem::remove (path, ec);
// Ignore errors - file may not exist
}

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +19
std::mutex mutex;
std::set<std::filesystem::path> registered_files;
bool atexit_registered = false;
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The atexit_registered flag should be std::atomic<bool> to ensure thread safety. Currently, it's a plain bool that's accessed under a mutex lock, but the mutex is only held during the registration check and insert operation. If multiple threads call create() concurrently, there's a potential race condition where the mutex is released after checking atexit_registered but before std::atexit() completes, allowing another thread to register the handler multiple times. Using std::atomic<bool> would provide proper memory ordering guarantees.

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +44
out << contents;
out.close ();
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The file write operation should verify that the write succeeded before closing. Currently, out << contents may fail silently (e.g., disk full, permissions issue after opening). Consider checking the stream state after writing and before closing to ensure data was actually written successfully.

Suggested change
out << contents;
out.close ();
out << contents;
if (!out)
{
throw std::runtime_error ("Unable to write to runtime file: " + path.string ());
}
out.close ();
if (!out)
{
throw std::runtime_error ("Unable to close runtime file: " + path.string ());
}

Copilot uses AI. Check for mistakes.
void nano::runtime_files::create_pid_file (std::filesystem::path const & path)
{
auto pid = boost::this_process::get_id ();
nano::runtime_files::create (path, std::to_string (pid));
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

The PID file content is written without a trailing newline, but when it's read in the test at systest/runtime_files.sh:38, the newline is explicitly stripped with tr -d '\n'. However, this is inconsistent with typical PID file conventions which usually include a trailing newline. Consider adding a newline after the PID for consistency with standard PID file format (i.e., change to std::to_string (pid) + "\n").

Suggested change
nano::runtime_files::create (path, std::to_string (pid));
nano::runtime_files::create (path, std::to_string (pid) + "\n");

Copilot uses AI. Check for mistakes.
Comment on lines +190 to +191
nano::runtime_files::create_runtime_info (*flags.runtime_info_file, info);
std::cerr << "Runtime info file created: " << *flags.runtime_info_file << std::endl;
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

If create_runtime_info() throws an exception (e.g., due to disk full, permission issues, or invalid path), the node will terminate via the catch block at line 225. However, the node will have already started and bound to ports. Consider wrapping this in a try-catch block to provide a more specific error message, or documenting that runtime_info_file creation failures are fatal and will terminate the node during startup.

Suggested change
nano::runtime_files::create_runtime_info (*flags.runtime_info_file, info);
std::cerr << "Runtime info file created: " << *flags.runtime_info_file << std::endl;
try
{
nano::runtime_files::create_runtime_info (*flags.runtime_info_file, info);
std::cerr << "Runtime info file created: " << *flags.runtime_info_file << std::endl;
}
catch (std::exception const & ex)
{
logger.critical (
nano::log::type::daemon,
"Failed to create runtime info file '{}': {}",
*flags.runtime_info_file,
ex.what ());
// Rethrow so that startup still fails as before
throw;
}

Copilot uses AI. Check for mistakes.
@pwojcikdev pwojcikdev force-pushed the system-test-port-conflicts branch from 9b83e98 to b708b54 Compare January 26, 2026 20:38
@pwojcikdev pwojcikdev force-pushed the system-test-port-conflicts branch from b708b54 to 1fe6305 Compare January 27, 2026 00:24
@pwojcikdev pwojcikdev merged commit a952ae2 into nanocurrency:develop Jan 27, 2026
25 of 26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli Changes related to the Command Line Interface documentation This item indicates the need for or supplies updated or expanded documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants