Skip to content

Conversation

@Rob-Hague
Copy link

@Rob-Hague Rob-Hague commented Feb 7, 2026

After the multi-url change, when configuring the address to connect to by setting SenderOptions.addr rather than by passing a config string, the configured value is ignored.

Fix by making addr return the first value in addresses (as it is documented to do).

closes #63

Summary by CodeRabbit

  • New Features

    • Enhanced address management with improved public API for accessing multiple addresses.
    • Added AddressCount property to query the number of configured addresses.
  • Bug Fixes

    • Refined address parsing logic with better default address handling.
    • Improved case-insensitive recognition in configuration parsing.

After the multi-url change, when configuring the address to connect to by setting
SenderOptions.addr rather than by passing a config string, the configured value is
ignored.

Fix by making addr return the first value in addresses (as it is documented to do).
@coderabbitai
Copy link

coderabbitai bot commented Feb 7, 2026

📝 Walkthrough

Walkthrough

This PR fixes a regression bug where SenderOptions.addr was ignored during initialization. The changes refactor address handling to use a collection-based approach, introducing public APIs for multi-address support while maintaining backward compatibility through a default address mechanism.

Changes

Cohort / File(s) Summary
Production Address Refactor
src/net-questdb-client/Utils/SenderOptions.cs
Replaced _addr field with readonly _addresses list. The addr property now gets/sets the first element of _addresses. Added public addresses property and AddressCount. Updated constructors to initialize with DefaultAddress constant and parse config addresses into the list, ensuring the list always contains at least the default address.
Test Updates
src/net-questdb-client-tests/SenderOptionsTests.cs
Added tests for default constructor initialization, addr property getter/setter, and address count validation. Updated BasicParse expectation from port 9000 to 9999 and modified DuplicateKey test to target non-addr keys.
Multi-URL Test Updates
src/net-questdb-client-tests/MultiUrlHttpTests.cs
Updated test expectations to verify case-insensitive "addr" key recognition and altered address parsing. Added explicit assertions on options.addr and updated address list expectations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • questdb/net-questdb-client#59: Both PRs introduce multi-address support with public addresses list and AddressCount properties, alongside corresponding test updates for multi-URL handling.

Poem

🐰 Addresses now listen with graceful care,
No longer lost in the localhost snare,
A collection of endpoints, first in the queue,
Your config respected—finally true!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Fix connecting via SenderOptions.addr' clearly identifies the main change: addressing the regression where SenderOptions.addr was being ignored.
Linked Issues check ✅ Passed The PR directly addresses issue #63 by restoring functionality to SenderOptions.addr so the client connects to the configured address instead of defaulting to localhost:9000.
Out of Scope Changes check ✅ Passed All changes are scoped to address handling in SenderOptions with corresponding test updates; no unrelated modifications detected beyond the stated objectives.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Comment on lines +62 to +64
// duplicate (non-addr) keys are 'last writer wins'
Assert.That(
new SenderOptions("http::addr=localhost:9000;addr=localhost:9009;").addr,
Is.EqualTo("localhost:9009"));
new SenderOptions("http::auth_timeout=15000;auth_timeout=8000;").auth_timeout,
Copy link
Author

Choose a reason for hiding this comment

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

This test is at odds with how addr is documented ("When multiple addresses are configured, this returns the first one."). So either the test needs to change or addr does. Went with the former

Copy link

@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: 1

🤖 Fix all issues with AI agents
In `@src/net-questdb-client/Utils/SenderOptions.cs`:
- Around line 59-60: SenderOptions (record) has a mutable List<string>
_addresses which is shallow-copied by the compiler-generated copy constructor
and causes aliasing when using with-expressions (affecting addr setter and
WithClientCert); fix by adding a custom copy constructor for SenderOptions that
deep-clones _addresses (e.g., create a new List<string> from other._addresses)
so copies get independent lists, and ensure any methods/properties that mutate
_addresses (addr property setter) operate on the instance's private list;
alternatively, replace the field with an immutable collection type (e.g.,
ImmutableList<string>) and change addr setter to reassign a new immutable list
instead of calling _addresses.Clear(), then adjust WithClientCert to rely on the
safe copy semantics.
🧹 Nitpick comments (2)
src/net-questdb-client/Utils/SenderOptions.cs (2)

162-167: addr setter does not guard against null.

_addresses.Add(value) will silently accept null since the parameter type is string (non-nullable reference type), but there's no runtime enforcement. A null addr would later cause NullReferenceException in Host, Port, and anywhere else that dereferences addr. Consider adding an ArgumentNullException guard.

🛡️ Proposed null guard
 set
 {
+    ArgumentNullException.ThrowIfNull(value);
     _addresses.Clear();
     _addresses.Add(value);
 }

600-611: Address values containing = are silently dropped.

Line 607 splits on = with RemoveEmptyEntries, so a param like addr=host=foo yields 3 parts, fails the kvp.Length == 2 check, and the address is silently ignored. While unlikely for addresses, this is inconsistent with how DbConnectionStringBuilder handles values (it only splits on the first =). Consider using Split('=', 2, ...) to limit the split count.

♻️ Proposed fix
-            var kvp = param.Split('=', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
-            if (kvp.Length == 2 && kvp[0].Equals("addr", StringComparison.OrdinalIgnoreCase))
+            var kvp = param.Split('=', 2, StringSplitOptions.TrimEntries);
+            if (kvp.Length == 2 && kvp[0].Equals("addr", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(kvp[1]))

Comment on lines +59 to +60
private const string DefaultAddress = "localhost:9000";
private readonly List<string> _addresses = new();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, find and examine the SenderOptions.cs file
fd -t f "SenderOptions.cs" --type f

Repository: questdb/net-questdb-client

Length of output: 115


🏁 Script executed:

# Get the file size and structure
wc -l src/net-questdb-client/Utils/SenderOptions.cs

Repository: questdb/net-questdb-client

Length of output: 119


🏁 Script executed:

# Check the record declaration and key lines
sed -n '1,70p' src/net-questdb-client/Utils/SenderOptions.cs

Repository: questdb/net-questdb-client

Length of output: 2817


🏁 Script executed:

# Check the addr property (around lines 162-167)
sed -n '160,175p' src/net-questdb-client/Utils/SenderOptions.cs

Repository: questdb/net-questdb-client

Length of output: 529


🏁 Script executed:

# Check the WithClientCert method (around line 731)
sed -n '725,740p' src/net-questdb-client/Utils/SenderOptions.cs

Repository: questdb/net-questdb-client

Length of output: 380


🏁 Script executed:

# Search for all 'with' expressions in C# files
rg -nP --type=cs '\bwith\s*\{' --glob '!**/obj/**' --glob '!**/bin/**'

Repository: questdb/net-questdb-client

Length of output: 214


🏁 Script executed:

# Check how many lines the file has and look at the end for any relevant code
tail -20 src/net-questdb-client/Utils/SenderOptions.cs

Repository: questdb/net-questdb-client

Length of output: 525


🏁 Script executed:

# Verify field initializer behavior in records and check if there's any evidence of actual mutations
rg -nP --type=cs '_addresses' src/net-questdb-client/Utils/SenderOptions.cs -B2 -A2

Repository: questdb/net-questdb-client

Length of output: 1477


🏁 Script executed:

# Check if there are usages of WithClientCert in tests or elsewhere that might expose the bug
rg -nP --type=cs 'WithClientCert' --glob '!**/obj/**' --glob '!**/bin/**' -B3 -A3

Repository: questdb/net-questdb-client

Length of output: 1297


🏁 Script executed:

# Check what tests exist and if they might be hitting this aliasing issue
rg -nP --type=cs '\.addr\s*=' --glob '!**/obj/**' --glob '!**/bin/**' -B2 -A2

Repository: questdb/net-questdb-client

Length of output: 520


Mutable List<string> field in record creates aliasing hazard with with expressions.

SenderOptions is a record type (line 43), so the compiler-synthesized copy constructor performs shallow copy of all fields. This means any instance created via with expression (e.g., WithClientCert at line 731) shares the same _addresses list reference. If the original or copy subsequently sets addr property, the setter's _addresses.Clear() call (line 165) mutates the shared list, corrupting the other instance's state.

Applies to lines 60 (field declaration), 162-167 (addr property), and 729-733 (WithClientCert).

Recommended fixes:

  1. Clone _addresses in a custom copy constructor.
  2. Replace List<string> with ImmutableList<string> and update the setter to reassign instead of mutate.
  3. Change SenderOptions from record to class if value semantics are not required.
🤖 Prompt for AI Agents
In `@src/net-questdb-client/Utils/SenderOptions.cs` around lines 59 - 60,
SenderOptions (record) has a mutable List<string> _addresses which is
shallow-copied by the compiler-generated copy constructor and causes aliasing
when using with-expressions (affecting addr setter and WithClientCert); fix by
adding a custom copy constructor for SenderOptions that deep-clones _addresses
(e.g., create a new List<string> from other._addresses) so copies get
independent lists, and ensure any methods/properties that mutate _addresses
(addr property setter) operate on the instance's private list; alternatively,
replace the field with an immutable collection type (e.g.,
ImmutableList<string>) and change addr setter to reassign a new immutable list
instead of calling _addresses.Clear(), then adjust WithClientCert to rely on the
safe copy semantics.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Last Release (3.2.0) is broken. SenderOptions.addr is ignored, causing fallback to localhost:9000 and IngressError

1 participant