This is an automated email from the ASF dual-hosted git repository.

hubcio pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iggy.git


The following commit(s) were added to refs/heads/master by this push:
     new 420047743 chore(server-ng): extract compio executor and io_uring 
diagnostics into shared crate (#3331)
420047743 is described below

commit 420047743c041171092bbcd00c1b7bd53760a136
Author: Jaya Kasa <[email protected]>
AuthorDate: Thu May 28 18:19:10 2026 -0400

    chore(server-ng): extract compio executor and io_uring diagnostics into 
shared crate (#3331)
---
 .../configs/src/configs_impl/typed_env_provider.rs |   2 +-
 core/server-ng/src/main.rs                         |  11 +-
 core/server/src/bootstrap.rs                       |  40 +--
 core/server/src/diagnostics.rs                     | 321 +--------------------
 core/{server => server_common}/src/diagnostics.rs  |  85 ++++--
 core/server_common/src/executor.rs                 | 132 +++++++++
 core/server_common/src/lib.rs                      |   3 +
 7 files changed, 199 insertions(+), 395 deletions(-)

diff --git a/core/configs/src/configs_impl/typed_env_provider.rs 
b/core/configs/src/configs_impl/typed_env_provider.rs
index 2dff687e6..772fb217a 100644
--- a/core/configs/src/configs_impl/typed_env_provider.rs
+++ b/core/configs/src/configs_impl/typed_env_provider.rs
@@ -55,7 +55,7 @@ const IGNORED_ENV_VARS: &[&str] = &[
     "IGGY_ROOT_PASSWORD",
     "IGGY_ROOT_USERNAME",
     // Tunes per-shard io_uring SQ/CQ capacity; read directly by
-    // `server::bootstrap::create_shard_executor` (see that fn for rationale).
+    // `server_common::executor::create_shard_executor` (see that fn for 
rationale).
     "IGGY_SHARD_RUNTIME_CAPACITY",
     "IGGY_TEST_CLEANUP_DISABLED",
     "IGGY_TEST_VERBOSE",
diff --git a/core/server-ng/src/main.rs b/core/server-ng/src/main.rs
index 6ac5ca2b1..b9a7c7da3 100644
--- a/core/server-ng/src/main.rs
+++ b/core/server-ng/src/main.rs
@@ -28,21 +28,18 @@ use server_ng::server_error::ServerNgError;
 use tracing::{error, info};
 
 fn main() -> Result<(), ServerNgError> {
-    // TODO(hubcio): decouple runtime creation from the `server` crate and
-    // move the shared compio executor setup into a lower-level crate/module
-    // used by both binaries.
-    let bootstrap_runtime = match server::bootstrap::create_shard_executor() {
+    let bootstrap_runtime = match server_common::create_shard_executor() {
         Ok(rt) => rt,
         Err(e) => {
             match e.kind() {
                 std::io::ErrorKind::InvalidInput => {
-                    server::diagnostics::print_invalid_io_uring_args_info();
+                    
server_common::diagnostics::print_invalid_io_uring_args_info();
                 }
                 std::io::ErrorKind::OutOfMemory => {
-                    server::diagnostics::print_locked_memory_limit_info();
+                    
server_common::diagnostics::print_locked_memory_limit_info();
                 }
                 std::io::ErrorKind::PermissionDenied => {
-                    server::diagnostics::print_io_uring_permission_info();
+                    
server_common::diagnostics::print_io_uring_permission_info();
                 }
                 _ => {}
             }
diff --git a/core/server/src/bootstrap.rs b/core/server/src/bootstrap.rs
index d00562aa7..fac9bed5e 100644
--- a/core/server/src/bootstrap.rs
+++ b/core/server/src/bootstrap.rs
@@ -50,7 +50,7 @@ use crate::{
         utils::{crypto, file::overwrite},
     },
 };
-use compio::{fs::create_dir_all, runtime::Runtime};
+use compio::fs::create_dir_all;
 use err_trail::ErrContext;
 use iggy_common::SemanticVersion;
 use iggy_common::{
@@ -170,43 +170,7 @@ pub fn create_root_user() -> User {
     User::root(&username, &password)
 }
 
-// Shard executors require IORING_SETUP_COOP_TASKRUN for predictable latency.
-// Falling back to default flags would silently degrade shard performance -
-// do not add a retry with reduced flags here.
-pub fn create_shard_executor() -> Result<Runtime, std::io::Error> {
-    // TODO: The event interval tick, could be configured based on the fact
-    // How many clients we expect to have connected.
-    // This roughly estimates the number of tasks we will create.
-    let mut proactor = compio::driver::ProactorBuilder::new();
-
-    // Each shard reserves io_uring SQ + CQ entries against `RLIMIT_MEMLOCK`.
-    // The multi-node integration tests spawn N nodes * M shards in a single
-    // process tree under a memlock budget that's often capped to 8 MiB on
-    // dev machines; the 4096-entry default blows past that and the OS
-    // returns `Cannot allocate memory` (EAGAIN/ENOMEM) during ring setup.
-    // The env knob lets the test harness shrink the per-ring footprint
-    // without compiling a separate test build. Production keeps the
-    // higher default for throughput.
-    let capacity = std::env::var("IGGY_SHARD_RUNTIME_CAPACITY")
-        .ok()
-        .and_then(|v| v.parse::<u32>().ok())
-        .unwrap_or(4096);
-    proactor
-        .capacity(capacity)
-        .coop_taskrun(true)
-        .taskrun_flag(true);
-
-    // FIXME(hubcio): Only set thread_pool_limit(0) on non-macOS platforms
-    // This causes a freeze on macOS with compio fs operations
-    // see https://github.com/compio-rs/compio/issues/446
-    #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
-    proactor.thread_pool_limit(0);
-
-    compio::runtime::RuntimeBuilder::new()
-        .with_proactor(proactor.to_owned())
-        .event_interval(128)
-        .build()
-}
+pub use server_common::create_shard_executor;
 
 pub fn resolve_persister(enforce_fsync: bool) -> Arc<PersisterKind> {
     match enforce_fsync {
diff --git a/core/server/src/diagnostics.rs b/core/server/src/diagnostics.rs
index 32a19ea3b..b2ed5ccdc 100644
--- a/core/server/src/diagnostics.rs
+++ b/core/server/src/diagnostics.rs
@@ -16,321 +16,6 @@
  * under the License.
  */
 
-#[cfg(target_os = "linux")]
-const DISCORD_SUPPORT_URL: &str = "https://discord.gg/apache-iggy";;
-
-#[cfg(target_os = "linux")]
-fn print_discord_link() {
-    eprintln!("  Need help? Join our Discord: {DISCORD_SUPPORT_URL}");
-    eprintln!();
-}
-
-/// Prints information about locked memory limits when runtime creation fails.
-/// This is typically needed when io_uring cannot allocate memory due to 
RLIMIT_MEMLOCK.
-#[cfg(target_os = "linux")]
-pub fn print_locked_memory_limit_info() {
-    use nix::sys::resource::{Resource, getrlimit};
-
-    let (soft, hard) = match getrlimit(Resource::RLIMIT_MEMLOCK) {
-        Ok(limits) => limits,
-        Err(_) => {
-            eprintln!("Failed to retrieve locked memory limits");
-            return;
-        }
-    };
-
-    let format_limit = |limit: u64| -> String {
-        if limit == u64::MAX {
-            "unlimited".to_string()
-        } else {
-            let kb = limit / 1024;
-            let mb = kb / 1024;
-            if mb > 0 {
-                format!("{} bytes ({} MB)", limit, mb)
-            } else {
-                format!("{} bytes ({} KB)", limit, kb)
-            }
-        }
-    };
-
-    eprintln!();
-    eprintln!("=== Locked Memory Limit Information ===");
-    eprintln!("Current soft limit: {}", format_limit(soft));
-    eprintln!("Current hard limit: {}", format_limit(hard));
-    eprintln!();
-    eprintln!("The io_uring runtime requires sufficient locked memory to 
operate.");
-    eprintln!("To increase the limit, you can:");
-    eprintln!();
-    eprintln!("  1. Temporarily (current session only):");
-    eprintln!("     ulimit -l unlimited");
-    eprintln!();
-    eprintln!("  2. Docker run:");
-    eprintln!("     docker run --ulimit memlock=-1:-1 ...");
-    eprintln!();
-    eprintln!("  3. Docker Compose (add to service):");
-    eprintln!("     ulimits:");
-    eprintln!("       memlock:");
-    eprintln!("         soft: -1");
-    eprintln!("         hard: -1");
-    eprintln!();
-    eprintln!("  4. Persistently (add to /etc/security/limits.conf):");
-    eprintln!("     * soft memlock unlimited");
-    eprintln!("     * hard memlock unlimited");
-    eprintln!();
-    eprintln!("  5. For systemd services (add to service file):");
-    eprintln!("     LimitMEMLOCK=infinity");
-    eprintln!();
-    print_discord_link();
-}
-
-/// Prints information about io_uring permission issues in containerized 
environments.
-/// This occurs when seccomp blocks io_uring syscalls.
-#[cfg(target_os = "linux")]
-pub fn print_io_uring_permission_info() {
-    eprintln!();
-    eprintln!("=== io_uring Permission Denied ===");
-    eprintln!();
-    eprintln!("The io_uring runtime requires specific syscalls that are 
blocked by default");
-    eprintln!("in containerized environments (Docker, Podman, etc.).");
-    eprintln!();
-    eprintln!("To resolve this issue:");
-    eprintln!();
-    eprintln!("  1. Docker Compose (add to service):");
-    eprintln!("     security_opt:");
-    eprintln!("       - seccomp:unconfined");
-    eprintln!();
-    eprintln!("  2. Docker run:");
-    eprintln!("     docker run --security-opt seccomp=unconfined ...");
-    eprintln!();
-    eprintln!("  3. Custom seccomp profile (more secure):");
-    eprintln!("     Create a profile allowing io_uring_setup, 
io_uring_enter,");
-    eprintln!("     and io_uring_register syscalls.");
-    eprintln!();
-    eprintln!("  4. Kubernetes (add to pod spec):");
-    eprintln!("     securityContext:");
-    eprintln!("       seccompProfile:");
-    eprintln!("         type: Unconfined");
-    eprintln!();
-    print_discord_link();
-}
-
-/// Minimum kernel version for IORING_SETUP_COOP_TASKRUN and 
IORING_SETUP_TASKRUN_FLAG.
-#[cfg(target_os = "linux")]
-const MIN_KERNEL_MAJOR: u32 = 5;
-#[cfg(target_os = "linux")]
-const MIN_KERNEL_MINOR: u32 = 19;
-
-/// Minimum kernel version for kernel.io_uring_disabled sysctl.
-#[cfg(target_os = "linux")]
-const SYSCTL_IO_URING_DISABLED_KERNEL_MAJOR: u32 = 6;
-#[cfg(target_os = "linux")]
-const SYSCTL_IO_URING_DISABLED_KERNEL_MINOR: u32 = 1;
-
-/// Prints diagnostic information when io_uring setup fails with EINVAL.
-///
-/// This typically occurs when the kernel does not support the io_uring flags
-/// required by shard executors (IORING_SETUP_COOP_TASKRUN, 
IORING_SETUP_TASKRUN_FLAG).
-/// The caller is responsible for deduplication (e.g., via `std::sync::Once`).
-#[cfg(target_os = "linux")]
-pub fn print_invalid_io_uring_args_info() {
-    use nix::sys::utsname::uname;
-    use std::fs;
-
-    eprintln!();
-    eprintln!("=== io_uring Invalid Argument (EINVAL) ===");
-    eprintln!();
-    eprintln!("The shard executor failed to initialize because the kernel 
rejected");
-    eprintln!("io_uring setup flags required for shard operation.");
-    eprintln!();
-    eprintln!("  The main thread's io_uring runtime uses default settings and 
initialized");
-    eprintln!("  successfully. Shard executors require additional flags:");
-    eprintln!("    - IORING_SETUP_COOP_TASKRUN (cooperative task running)");
-    eprintln!("    - IORING_SETUP_TASKRUN_FLAG (task runner flag 
notification)");
-    eprintln!(
-        "  These flags require Linux kernel >= 
{MIN_KERNEL_MAJOR}.{MIN_KERNEL_MINOR} with full io_uring support."
-    );
-    eprintln!();
-
-    let mut detected_issues: Vec<String> = Vec::new();
-
-    // 1. Kernel version check
-    let uname_info = match uname() {
-        Ok(info) => Some(info),
-        Err(_) => {
-            eprintln!("  [!] Could not retrieve kernel information via 
uname(2).");
-            None
-        }
-    };
-
-    let mut kernel_version: Option<(u32, u32)> = None;
-
-    if let Some(ref info) = uname_info {
-        let release = info.release().to_string_lossy();
-        eprintln!("  Kernel release: {release}");
-
-        if let Some((major, minor)) = parse_kernel_version(&release) {
-            kernel_version = Some((major, minor));
-            if (major, minor) < (MIN_KERNEL_MAJOR, MIN_KERNEL_MINOR) {
-                detected_issues.push(format!(
-                    "Kernel {major}.{minor} is too old (need >= 
{MIN_KERNEL_MAJOR}.{MIN_KERNEL_MINOR})"
-                ));
-            }
-        } else {
-            eprintln!("  [!] Could not parse kernel version from release 
string.");
-        }
-
-        // 2. WSL2 detection
-        let release_is_wsl = release.contains("microsoft") || 
release.contains("Microsoft");
-        let proc_version_is_wsl = fs::read_to_string("/proc/version")
-            .map(|v| v.contains("Microsoft") || v.contains("microsoft"))
-            .unwrap_or(false);
-
-        if release_is_wsl || proc_version_is_wsl {
-            eprintln!("  Environment: WSL2 (Microsoft kernel fork detected)");
-            detected_issues.push(
-                "WSL2 kernel may not support IORING_SETUP_COOP_TASKRUN even if 
version >= 5.19"
-                    .to_string(),
-            );
-        }
-    }
-
-    // 3. kernel.io_uring_disabled sysctl (available since kernel 6.1)
-    match fs::read_to_string("/proc/sys/kernel/io_uring_disabled") {
-        Ok(value) => {
-            let value = value.trim();
-            eprintln!("  kernel.io_uring_disabled = {value}");
-            match value {
-                "1" => detected_issues
-                    .push("io_uring is disabled for unprivileged users (sysctl 
= 1)".to_string()),
-                "2" => detected_issues
-                    .push("io_uring is fully disabled by sysctl (sysctl = 
2)".to_string()),
-                _ => {}
-            }
-        }
-        Err(_) => {
-            // The sysctl was introduced in kernel 6.1. If the file is absent 
on a kernel >= 6.1,
-            // io_uring is likely not compiled in (CONFIG_IO_URING=n).
-            if let Some((major, minor)) = kernel_version
-                && (major, minor)
-                    >= (
-                        SYSCTL_IO_URING_DISABLED_KERNEL_MAJOR,
-                        SYSCTL_IO_URING_DISABLED_KERNEL_MINOR,
-                    )
-            {
-                detected_issues.push(format!(
-                    "kernel.io_uring_disabled sysctl not found on kernel >= \
-                     
{SYSCTL_IO_URING_DISABLED_KERNEL_MAJOR}.{SYSCTL_IO_URING_DISABLED_KERNEL_MINOR} 
\
-                     - io_uring may not be compiled in (CONFIG_IO_URING=n)"
-                ));
-            }
-        }
-    }
-
-    // 4. AppArmor - informational only, not added to detected_issues
-    let apparmor_profile = 
fs::read_to_string("/proc/self/attr/apparmor/current")
-        .ok()
-        .map(|s| s.trim().to_string());
-
-    if let Some(ref profile) = apparmor_profile
-        && profile != "unconfined"
-        && !profile.is_empty()
-    {
-        eprintln!("  AppArmor profile: {profile}");
-    }
-
-    // Print detected issues
-    if detected_issues.is_empty() {
-        eprintln!();
-        eprintln!("  No specific issue was detected. The kernel may lack 
io_uring support");
-        eprintln!("  for the flags used by Iggy's shard executors.");
-    } else {
-        eprintln!();
-        eprintln!("  Detected issues:");
-        for (i, issue) in detected_issues.iter().enumerate() {
-            eprintln!("    {}. {issue}", i + 1);
-        }
-    }
-
-    eprintln!();
-    eprintln!("  To resolve this:");
-    eprintln!();
-    eprintln!(
-        "  1. Upgrade to Linux kernel >= {MIN_KERNEL_MAJOR}.{MIN_KERNEL_MINOR} 
(>= 
{SYSCTL_IO_URING_DISABLED_KERNEL_MAJOR}.{SYSCTL_IO_URING_DISABLED_KERNEL_MINOR} 
recommended)"
-    );
-    eprintln!();
-    eprintln!("  2. If running under WSL2:");
-    eprintln!("     - Update WSL: wsl --update  (from PowerShell)");
-    eprintln!("     - Or build a custom kernel with full io_uring support:");
-    eprintln!("       
https://learn.microsoft.com/en-us/windows/wsl/wsl-config#wsl-2-settings";);
-    eprintln!("     - Or use Docker Desktop / a native Linux VM instead of 
WSL2");
-    eprintln!();
-    eprintln!("  3. If io_uring is disabled via sysctl:");
-    eprintln!("     sudo sysctl -w kernel.io_uring_disabled=0");
-    eprintln!();
-    eprintln!("  4. If AppArmor is restricting io_uring:");
-    eprintln!("     sudo aa-complain <profile-name>");
-    eprintln!();
-    eprintln!("  5. Check kernel logs for more details:");
-    eprintln!("     dmesg | grep -i io_uring");
-    eprintln!();
-    print_discord_link();
-}
-
-/// Parses "major.minor[.patch...][-suffix]" from a kernel release string.
-#[cfg(target_os = "linux")]
-fn parse_kernel_version(release: &str) -> Option<(u32, u32)> {
-    let mut parts = release
-        .split(|c: char| !c.is_ascii_digit())
-        .filter(|s| !s.is_empty());
-    let major = parts.next()?.parse::<u32>().ok()?;
-    let minor = parts.next()?.parse::<u32>().ok()?;
-    Some((major, minor))
-}
-
-#[cfg(not(target_os = "linux"))]
-pub fn print_locked_memory_limit_info() {}
-
-#[cfg(not(target_os = "linux"))]
-pub fn print_io_uring_permission_info() {}
-
-#[cfg(not(target_os = "linux"))]
-pub fn print_invalid_io_uring_args_info() {}
-
-#[cfg(test)]
-mod tests {
-    #[cfg(target_os = "linux")]
-    use super::parse_kernel_version;
-
-    #[test]
-    #[cfg(target_os = "linux")]
-    fn test_parse_standard_kernel_version() {
-        assert_eq!(parse_kernel_version("6.8.0-45-generic"), Some((6, 8)));
-    }
-
-    #[test]
-    #[cfg(target_os = "linux")]
-    fn test_parse_wsl2_kernel_version() {
-        assert_eq!(
-            parse_kernel_version("5.15.153.1-microsoft-standard-WSL2"),
-            Some((5, 15))
-        );
-    }
-
-    #[test]
-    #[cfg(target_os = "linux")]
-    fn test_parse_minimal_version() {
-        assert_eq!(parse_kernel_version("5.19"), Some((5, 19)));
-    }
-
-    #[test]
-    #[cfg(target_os = "linux")]
-    fn test_parse_garbage_returns_none() {
-        assert_eq!(parse_kernel_version("not-a-version"), None);
-    }
-
-    #[test]
-    #[cfg(target_os = "linux")]
-    fn test_parse_empty_returns_none() {
-        assert_eq!(parse_kernel_version(""), None);
-    }
-}
+pub use server_common::diagnostics::print_invalid_io_uring_args_info;
+pub use server_common::diagnostics::print_io_uring_permission_info;
+pub use server_common::diagnostics::print_locked_memory_limit_info;
diff --git a/core/server/src/diagnostics.rs 
b/core/server_common/src/diagnostics.rs
similarity index 86%
copy from core/server/src/diagnostics.rs
copy to core/server_common/src/diagnostics.rs
index 32a19ea3b..568d4e44d 100644
--- a/core/server/src/diagnostics.rs
+++ b/core/server_common/src/diagnostics.rs
@@ -1,4 +1,5 @@
-/* Licensed to the Apache Software Foundation (ASF) under one
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
  * regarding copyright ownership.  The ASF licenses this file
@@ -25,6 +26,27 @@ fn print_discord_link() {
     eprintln!();
 }
 
+/// Formats an `RLIMIT_MEMLOCK` value for diagnostic output.
+///
+/// `u64::MAX` is rendered as `unlimited` (the value getrlimit returns
+/// for an uncapped limit); other values are rendered as raw bytes plus
+/// a coarse MB-or-KB suffix so operators can eyeball whether the limit
+/// is in the ballpark of the 4096-entry io_uring ring footprint.
+#[cfg(target_os = "linux")]
+fn format_limit(limit: u64) -> String {
+    if limit == u64::MAX {
+        "unlimited".to_string()
+    } else {
+        let kb = limit / 1024;
+        let mb = kb / 1024;
+        if mb > 0 {
+            format!("{limit} bytes ({mb} MB)")
+        } else {
+            format!("{limit} bytes ({kb} KB)")
+        }
+    }
+}
+
 /// Prints information about locked memory limits when runtime creation fails.
 /// This is typically needed when io_uring cannot allocate memory due to 
RLIMIT_MEMLOCK.
 #[cfg(target_os = "linux")]
@@ -39,20 +61,6 @@ pub fn print_locked_memory_limit_info() {
         }
     };
 
-    let format_limit = |limit: u64| -> String {
-        if limit == u64::MAX {
-            "unlimited".to_string()
-        } else {
-            let kb = limit / 1024;
-            let mb = kb / 1024;
-            if mb > 0 {
-                format!("{} bytes ({} MB)", limit, mb)
-            } else {
-                format!("{} bytes ({} KB)", limit, kb)
-            }
-        }
-    };
-
     eprintln!();
     eprintln!("=== Locked Memory Limit Information ===");
     eprintln!("Current soft limit: {}", format_limit(soft));
@@ -288,28 +296,25 @@ fn parse_kernel_version(release: &str) -> Option<(u32, 
u32)> {
 }
 
 #[cfg(not(target_os = "linux"))]
-pub fn print_locked_memory_limit_info() {}
+pub const fn print_locked_memory_limit_info() {}
 
 #[cfg(not(target_os = "linux"))]
-pub fn print_io_uring_permission_info() {}
+pub const fn print_io_uring_permission_info() {}
 
 #[cfg(not(target_os = "linux"))]
-pub fn print_invalid_io_uring_args_info() {}
+pub const fn print_invalid_io_uring_args_info() {}
 
-#[cfg(test)]
+#[cfg(all(test, target_os = "linux"))]
 mod tests {
-    #[cfg(target_os = "linux")]
-    use super::parse_kernel_version;
+    use super::{format_limit, parse_kernel_version};
 
     #[test]
-    #[cfg(target_os = "linux")]
-    fn test_parse_standard_kernel_version() {
+    fn parse_kernel_version_standard() {
         assert_eq!(parse_kernel_version("6.8.0-45-generic"), Some((6, 8)));
     }
 
     #[test]
-    #[cfg(target_os = "linux")]
-    fn test_parse_wsl2_kernel_version() {
+    fn parse_kernel_version_wsl2() {
         assert_eq!(
             parse_kernel_version("5.15.153.1-microsoft-standard-WSL2"),
             Some((5, 15))
@@ -317,20 +322,38 @@ mod tests {
     }
 
     #[test]
-    #[cfg(target_os = "linux")]
-    fn test_parse_minimal_version() {
+    fn parse_kernel_version_minimal() {
         assert_eq!(parse_kernel_version("5.19"), Some((5, 19)));
     }
 
     #[test]
-    #[cfg(target_os = "linux")]
-    fn test_parse_garbage_returns_none() {
+    fn parse_kernel_version_garbage_returns_none() {
         assert_eq!(parse_kernel_version("not-a-version"), None);
     }
 
     #[test]
-    #[cfg(target_os = "linux")]
-    fn test_parse_empty_returns_none() {
+    fn parse_kernel_version_empty_returns_none() {
         assert_eq!(parse_kernel_version(""), None);
     }
+
+    #[test]
+    fn parse_kernel_version_overflow_returns_none() {
+        // u32::MAX + 1 in the major slot must not silently wrap.
+        assert_eq!(parse_kernel_version("4294967296.0"), None);
+    }
+
+    #[test]
+    fn format_limit_unlimited() {
+        assert_eq!(format_limit(u64::MAX), "unlimited");
+    }
+
+    #[test]
+    fn format_limit_sub_mb_uses_kb_suffix() {
+        assert_eq!(format_limit(64 * 1024), "65536 bytes (64 KB)");
+    }
+
+    #[test]
+    fn format_limit_mb_range_uses_mb_suffix() {
+        assert_eq!(format_limit(8 * 1024 * 1024), "8388608 bytes (8 MB)");
+    }
 }
diff --git a/core/server_common/src/executor.rs 
b/core/server_common/src/executor.rs
new file mode 100644
index 000000000..c6e20e05e
--- /dev/null
+++ b/core/server_common/src/executor.rs
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use compio::runtime::Runtime;
+
+const DEFAULT_SHARD_RUNTIME_CAPACITY: u32 = 4096;
+const SHARD_RUNTIME_CAPACITY_ENV: &str = "IGGY_SHARD_RUNTIME_CAPACITY";
+
+/// Resolves the per-shard io_uring SQ/CQ capacity from 
`IGGY_SHARD_RUNTIME_CAPACITY`,
+/// falling back to [`DEFAULT_SHARD_RUNTIME_CAPACITY`] when the var is missing 
or
+/// fails to parse as `u32`.
+fn shard_capacity_from_env() -> u32 {
+    std::env::var(SHARD_RUNTIME_CAPACITY_ENV)
+        .ok()
+        .and_then(|v| v.parse::<u32>().ok())
+        .unwrap_or(DEFAULT_SHARD_RUNTIME_CAPACITY)
+}
+
+/// Creates a compio runtime for a shard thread, with shard-specific 
`io_uring` flags.
+///
+/// The per-ring SQ/CQ capacity defaults to `4096` and can be overridden via 
the
+/// `IGGY_SHARD_RUNTIME_CAPACITY` env var, which the multi-node integration
+/// harness sets to `256` so N nodes * M shards fit under an 8 MiB
+/// `RLIMIT_MEMLOCK` budget without `ENOMEM` at ring setup.
+///
+/// # Errors
+///
+/// Returns an `std::io::Error` if the underlying `io_uring` proactor cannot 
be initialised.
+/// On `InvalidInput` the kernel rejected the required flags; on `OutOfMemory` 
or
+/// `PermissionDenied` the caller should print the appropriate diagnostic 
before panicking.
+///
+/// Shard executors require `IORING_SETUP_COOP_TASKRUN` for predictable 
latency.
+/// Falling back to default flags would silently degrade shard performance -
+/// do not add a retry with reduced flags here.
+pub fn create_shard_executor() -> Result<Runtime, std::io::Error> {
+    // TODO: The event interval tick, could be configured based on the fact
+    // How many clients we expect to have connected.
+    // This roughly estimates the number of tasks we will create.
+    let mut proactor = compio::driver::ProactorBuilder::new();
+
+    proactor
+        .capacity(shard_capacity_from_env())
+        .coop_taskrun(true)
+        .taskrun_flag(true);
+
+    // FIXME(hubcio): Only set thread_pool_limit(0) on non-macOS platforms
+    // This causes a freeze on macOS with compio fs operations
+    // see https://github.com/compio-rs/compio/issues/446
+    #[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
+    proactor.thread_pool_limit(0);
+
+    compio::runtime::RuntimeBuilder::new()
+        .with_proactor(proactor.to_owned())
+        .event_interval(128)
+        .build()
+}
+
+#[cfg(test)]
+mod tests {
+    use super::{
+        DEFAULT_SHARD_RUNTIME_CAPACITY, SHARD_RUNTIME_CAPACITY_ENV, 
shard_capacity_from_env,
+    };
+    use serial_test::serial;
+
+    fn with_capacity_env<R>(value: Option<&str>, f: impl FnOnce() -> R) -> R {
+        // SAFETY: tests in this module are #[serial], so no other thread races
+        // on the process-wide environment while the guard is active.
+        let prev = std::env::var(SHARD_RUNTIME_CAPACITY_ENV).ok();
+        unsafe {
+            match value {
+                Some(v) => std::env::set_var(SHARD_RUNTIME_CAPACITY_ENV, v),
+                None => std::env::remove_var(SHARD_RUNTIME_CAPACITY_ENV),
+            }
+        }
+        let out = f();
+        unsafe {
+            match prev {
+                Some(v) => std::env::set_var(SHARD_RUNTIME_CAPACITY_ENV, v),
+                None => std::env::remove_var(SHARD_RUNTIME_CAPACITY_ENV),
+            }
+        }
+        out
+    }
+
+    #[test]
+    #[serial]
+    fn shard_capacity_from_env_uses_parsed_value() {
+        with_capacity_env(Some("256"), || {
+            assert_eq!(shard_capacity_from_env(), 256);
+        });
+    }
+
+    #[test]
+    #[serial]
+    fn shard_capacity_from_env_falls_back_when_unset() {
+        with_capacity_env(None, || {
+            assert_eq!(shard_capacity_from_env(), 
DEFAULT_SHARD_RUNTIME_CAPACITY);
+        });
+    }
+
+    #[test]
+    #[serial]
+    fn shard_capacity_from_env_falls_back_on_unparsable() {
+        with_capacity_env(Some("not-a-number"), || {
+            assert_eq!(shard_capacity_from_env(), 
DEFAULT_SHARD_RUNTIME_CAPACITY);
+        });
+    }
+
+    #[test]
+    #[serial]
+    fn shard_capacity_from_env_falls_back_on_negative() {
+        with_capacity_env(Some("-1"), || {
+            assert_eq!(shard_capacity_from_env(), 
DEFAULT_SHARD_RUNTIME_CAPACITY);
+        });
+    }
+}
diff --git a/core/server_common/src/lib.rs b/core/server_common/src/lib.rs
index b11447e59..75be8cb01 100644
--- a/core/server_common/src/lib.rs
+++ b/core/server_common/src/lib.rs
@@ -20,6 +20,8 @@ mod buffer;
 mod certificates;
 mod consensus_message;
 mod deduplication;
+pub mod diagnostics;
+mod executor;
 mod in_flight;
 mod indexes_mut;
 // TODO(hubcio): iobuf was relocated verbatim from 
`core/binary_protocol/src/consensus/iobuf.rs`
@@ -50,6 +52,7 @@ pub use consensus_message::{
     MutableBacking, RequestBacking, RequestBackingKind, ResponseBacking, 
ResponseBackingKind,
 };
 pub use deduplication::MessageDeduplicator;
+pub use executor::create_shard_executor;
 pub use in_flight::IggyMessagesBatchSetInFlight;
 pub use indexes_mut::IggyIndexesMut;
 pub use memory_pool::{MEMORY_POOL, MemoryPool, MemoryPoolConfigOther, 
memory_pool};

Reply via email to