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

fresh-borzoni pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new 7d71d6ba [elixir] feat: Extend Fluss.Config with security, SASL and 
connect timeout options (#577)
7d71d6ba is described below

commit 7d71d6ba3f59e96da7d39ec7eab3227295a45af5
Author: Nicoleta Lazar <[email protected]>
AuthorDate: Fri May 29 00:36:13 2026 +0100

    [elixir] feat: Extend Fluss.Config with security, SASL and connect timeout 
options (#577)
    
    * feat(elixir): Extend NifConfig with security & connect timeout fields
    
    This commit adds 5 fields to NifConfig: connect timeout ms and
    security_* settings (protocol, sasl mechanism, usernama and password)
    
    * feat(elixir): Expose security opts and connect timeout in Fluss.Config
    
    This commit adds 5 fields to Fluss.Config with the corresponding
    setters: connect timeout, security protocol, sasl mechanism, username
    and password.
    
    To ensure that the password is redacted, we add a custom Inspect impl,
    essentially mirroring the upstream rust debug redaction.
---
 bindings/elixir/lib/fluss/config.ex            | 54 +++++++++++++++++++++++++
 bindings/elixir/native/fluss_nif/src/config.rs | 20 +++++++++
 bindings/elixir/test/config_test.exs           | 56 ++++++++++++++++++++++++++
 3 files changed, 130 insertions(+)

diff --git a/bindings/elixir/lib/fluss/config.ex 
b/bindings/elixir/lib/fluss/config.ex
index 07324df0..8aaacf79 100644
--- a/bindings/elixir/lib/fluss/config.ex
+++ b/bindings/elixir/lib/fluss/config.ex
@@ -33,6 +33,7 @@ defmodule Fluss.Config do
 
   @enforce_keys [:bootstrap_servers]
   defstruct bootstrap_servers: nil,
+            connect_timeout_ms: nil,
             remote_file_download_thread_num: nil,
             scanner_log_fetch_max_bytes: nil,
             scanner_log_fetch_max_bytes_for_bucket: nil,
@@ -41,6 +42,10 @@ defmodule Fluss.Config do
             scanner_log_max_poll_records: nil,
             scanner_remote_log_prefetch_num: nil,
             scanner_remote_log_read_concurrency: nil,
+            security_protocol: nil,
+            security_sasl_mechanism: nil,
+            security_sasl_password: nil,
+            security_sasl_username: nil,
             writer_acks: nil,
             writer_batch_size: nil,
             writer_batch_timeout_ms: nil,
@@ -56,6 +61,7 @@ defmodule Fluss.Config do
 
   @type t :: %__MODULE__{
           bootstrap_servers: String.t(),
+          connect_timeout_ms: non_neg_integer() | nil,
           remote_file_download_thread_num: non_neg_integer() | nil,
           scanner_log_fetch_max_bytes: non_neg_integer() | nil,
           scanner_log_fetch_max_bytes_for_bucket: non_neg_integer() | nil,
@@ -64,6 +70,10 @@ defmodule Fluss.Config do
           scanner_log_max_poll_records: non_neg_integer() | nil,
           scanner_remote_log_prefetch_num: non_neg_integer() | nil,
           scanner_remote_log_read_concurrency: non_neg_integer() | nil,
+          security_protocol: String.t() | nil,
+          security_sasl_mechanism: String.t() | nil,
+          security_sasl_password: String.t() | nil,
+          security_sasl_username: String.t() | nil,
           writer_acks: String.t() | nil,
           writer_batch_size: non_neg_integer() | nil,
           writer_batch_timeout_ms: non_neg_integer() | nil,
@@ -90,6 +100,10 @@ defmodule Fluss.Config do
   def set_bootstrap_servers(%__MODULE__{} = config, servers) when 
is_binary(servers),
     do: %{config | bootstrap_servers: servers}
 
+  @spec set_connect_timeout_ms(t(), non_neg_integer()) :: t()
+  def set_connect_timeout_ms(%__MODULE__{} = config, ms) when is_integer(ms),
+    do: %{config | connect_timeout_ms: ms}
+
   @spec set_remote_file_download_thread_num(t(), non_neg_integer()) :: t()
   def set_remote_file_download_thread_num(%__MODULE__{} = config, threads)
       when is_integer(threads),
@@ -128,6 +142,22 @@ defmodule Fluss.Config do
       when is_integer(concurrency),
       do: %{config | scanner_remote_log_read_concurrency: concurrency}
 
+  @spec set_security_protocol(t(), String.t()) :: t()
+  def set_security_protocol(%__MODULE__{} = config, protocol) when 
is_binary(protocol),
+    do: %{config | security_protocol: protocol}
+
+  @spec set_security_sasl_mechanism(t(), String.t()) :: t()
+  def set_security_sasl_mechanism(%__MODULE__{} = config, mechanism) when 
is_binary(mechanism),
+    do: %{config | security_sasl_mechanism: mechanism}
+
+  @spec set_security_sasl_password(t(), String.t()) :: t()
+  def set_security_sasl_password(%__MODULE__{} = config, pass) when 
is_binary(pass),
+    do: %{config | security_sasl_password: pass}
+
+  @spec set_security_sasl_username(t(), String.t()) :: t()
+  def set_security_sasl_username(%__MODULE__{} = config, username) when 
is_binary(username),
+    do: %{config | security_sasl_username: username}
+
   @spec set_writer_acks(t(), String.t()) :: t()
   def set_writer_acks(%__MODULE__{} = config, acks) when is_binary(acks),
     do: %{config | writer_acks: acks}
@@ -183,3 +213,27 @@ defmodule Fluss.Config do
   @spec get_bootstrap_servers(t()) :: String.t()
   def get_bootstrap_servers(%__MODULE__{bootstrap_servers: servers}), do: 
servers
 end
+
+defimpl Inspect, for: Fluss.Config do
+  import Inspect.Algebra
+
+  def inspect(%Fluss.Config{} = config, opts) do
+    sanitized = %{config | security_sasl_password: 
redact(config.security_sasl_password)}
+
+    fields = sanitized |> Map.from_struct() |> Map.to_list()
+
+    container_doc(
+      "%Fluss.Config{",
+      fields,
+      "}",
+      opts,
+      fn {key, value}, opts ->
+        concat([Atom.to_string(key), ": ", to_doc(value, opts)])
+      end,
+      separator: ","
+    )
+  end
+
+  defp redact(nil), do: nil
+  defp redact(_), do: "[REDACTED]"
+end
diff --git a/bindings/elixir/native/fluss_nif/src/config.rs 
b/bindings/elixir/native/fluss_nif/src/config.rs
index 79aabcd7..8c1bab51 100644
--- a/bindings/elixir/native/fluss_nif/src/config.rs
+++ b/bindings/elixir/native/fluss_nif/src/config.rs
@@ -31,6 +31,7 @@ pub enum NifNoKeyAssigner {
 #[module = "Fluss.Config"]
 pub struct NifConfig {
     pub bootstrap_servers: String,
+    pub connect_timeout_ms: Option<u64>,
     pub remote_file_download_thread_num: Option<u64>,
     pub scanner_log_fetch_max_bytes: Option<i32>,
     pub scanner_log_fetch_max_bytes_for_bucket: Option<i32>,
@@ -39,6 +40,10 @@ pub struct NifConfig {
     pub scanner_log_max_poll_records: Option<u64>,
     pub scanner_remote_log_prefetch_num: Option<u64>,
     pub scanner_remote_log_read_concurrency: Option<u64>,
+    pub security_protocol: Option<String>,
+    pub security_sasl_mechanism: Option<String>,
+    pub security_sasl_password: Option<String>,
+    pub security_sasl_username: Option<String>,
     pub writer_acks: Option<String>,
     pub writer_batch_size: Option<i32>,
     pub writer_batch_timeout_ms: Option<i64>,
@@ -59,6 +64,9 @@ impl NifConfig {
             bootstrap_servers: self.bootstrap_servers,
             ..Config::default()
         };
+        if let Some(timeout) = self.connect_timeout_ms {
+            config.connect_timeout_ms = timeout;
+        }
         if let Some(n) = self.remote_file_download_thread_num {
             config.remote_file_download_thread_num = n as usize;
         }
@@ -83,6 +91,18 @@ impl NifConfig {
         if let Some(n) = self.scanner_remote_log_read_concurrency {
             config.scanner_remote_log_read_concurrency = n as usize;
         }
+        if let Some(protocol) = self.security_protocol {
+            config.security_protocol = protocol;
+        }
+        if let Some(mechanism) = self.security_sasl_mechanism {
+            config.security_sasl_mechanism = mechanism;
+        }
+        if let Some(password) = self.security_sasl_password {
+            config.security_sasl_password = password;
+        }
+        if let Some(username) = self.security_sasl_username {
+            config.security_sasl_username = username;
+        }
         if let Some(size) = self.writer_batch_size {
             config.writer_batch_size = size;
         }
diff --git a/bindings/elixir/test/config_test.exs 
b/bindings/elixir/test/config_test.exs
index 2550a7e8..f4b8a11c 100644
--- a/bindings/elixir/test/config_test.exs
+++ b/bindings/elixir/test/config_test.exs
@@ -23,6 +23,14 @@ defmodule Fluss.ConfigTest do
     assert config == %Fluss.Config{bootstrap_servers: "localhost:9123"}
   end
 
+  test "set_connect_timeout_ms/2 sets the connect timeout" do
+    config =
+      Fluss.Config.new("localhost:9123")
+      |> Fluss.Config.set_connect_timeout_ms(30_000)
+
+    assert config.connect_timeout_ms == 30_000
+  end
+
   test "set_remote_file_download_thread_num/2 sets the download thread num" do
     config =
       Fluss.Config.new("localhost:9123")
@@ -87,6 +95,54 @@ defmodule Fluss.ConfigTest do
     assert config.scanner_remote_log_read_concurrency == 4
   end
 
+  test "set_security_protocol/2 sets the security protocol" do
+    config =
+      Fluss.Config.new("localhost:9123")
+      |> Fluss.Config.set_security_protocol("sasl")
+
+    assert config.security_protocol == "sasl"
+  end
+
+  test "set_security_sasl_mechanism/2 sets the SASL mechanism" do
+    config =
+      Fluss.Config.new("localhost:9123")
+      |> Fluss.Config.set_security_sasl_mechanism("PLAIN")
+
+    assert config.security_sasl_mechanism == "PLAIN"
+  end
+
+  test "set_security_sasl_username/2 sets the SASL username" do
+    config =
+      Fluss.Config.new("localhost:9123")
+      |> Fluss.Config.set_security_sasl_username("admin")
+
+    assert config.security_sasl_username == "admin"
+  end
+
+  test "set_security_sasl_password/2 sets the SASL password" do
+    config =
+      Fluss.Config.new("localhost:9123")
+      |> Fluss.Config.set_security_sasl_password("secret")
+
+    assert config.security_sasl_password == "secret"
+  end
+
+  test "inspect/1 redacts security_sasl_password when set" do
+    config =
+      Fluss.Config.new("localhost:9123")
+      |> Fluss.Config.set_security_sasl_password("supersecret")
+
+    output = inspect(config)
+    refute output =~ "supersecret"
+    assert output =~ "[REDACTED]"
+  end
+
+  test "inspect/1 leaves nil security_sasl_password as nil" do
+    config = Fluss.Config.new("localhost:9123")
+    output = inspect(config)
+    assert output =~ "security_sasl_password: nil"
+  end
+
   test "set_writer_acks/2 sets the acks value" do
     config =
       Fluss.Config.new("localhost:9123")

Reply via email to