Skip to content

Commit eb020d0

Browse files
Fix LWWDictionary.Delta ArgumentNullException when underlying delta is null (#7910) (#7911) (#7912)
## Summary Fixed a bug where LWWDictionary.Delta would throw ArgumentNullException when the underlying ORDictionary.Delta is null, which is a legitimate state after initialization or calling ResetDelta(). ## Changes - Modified LWWDictionary.Delta property to return null when Underlying.Delta is null - Added test Bugfix_7910_LWWDictionary_Delta_should_handle_null_underlying_delta to verify the fix ## Root Cause The LWWDictionary.Delta property was unconditionally wrapping Underlying.Delta in a LWWDictionaryDelta constructor, which throws ArgumentNullException when passed null. However, ORDictionary.Delta can legitimately return null after construction or ResetDelta(). The Replicator expects deltas to be nullable (uses ?? operator at lines 665 and 685), so this fix aligns LWWDictionary with the expected interface contract. Fixes #7910
1 parent 53e1b3d commit eb020d0

File tree

2 files changed

+30
-2
lines changed

2 files changed

+30
-2
lines changed

src/contrib/cluster/Akka.DistributedData.Tests/LWWDictionarySpec.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,5 +161,33 @@ public async Task Bugfix_4400_LWWDictionary_Deltas_must_merge_other_LWWDictionar
161161
merged1.Entries["b"].Should().BeEquivalentTo("B2");
162162
merged1.Entries["c"].Should().BeEquivalentTo("C");
163163
}
164+
165+
/// <summary>
166+
/// Bug reproduction: https://github.com/akkadotnet/akka.net/issues/7910
167+
/// LWWDictionary.Delta should return null when underlying ORDictionary.Delta is null
168+
/// </summary>
169+
[Fact]
170+
public void Bugfix_7910_LWWDictionary_Delta_should_handle_null_underlying_delta()
171+
{
172+
// Empty dictionary has no delta
173+
var empty = LWWDictionary<string, string>.Empty;
174+
empty.Delta.Should().BeNull("empty dictionary should have null delta");
175+
176+
// After ResetDelta(), delta should be null
177+
var m1 = LWWDictionary<string, string>.Empty
178+
.SetItem(_node1, "a", "A")
179+
.SetItem(_node1, "b", "B");
180+
181+
m1.Delta.Should().NotBeNull("dictionary with modifications should have a delta");
182+
183+
var m2 = m1.ResetDelta();
184+
m2.Delta.Should().BeNull("after ResetDelta(), delta should be null");
185+
186+
// Verify the dictionary still contains the data
187+
m2.ContainsKey("a").Should().BeTrue();
188+
m2.ContainsKey("b").Should().BeTrue();
189+
m2["a"].Should().Be("A");
190+
m2["b"].Should().Be("B");
191+
}
164192
}
165193
}

src/contrib/cluster/Akka.DistributedData/LWWDictionary.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,8 +402,8 @@ public override int GetHashCode()
402402
}
403403

404404
// TODO: optimize this so it doesn't allocate each time it's called
405-
public ORDictionary<TKey, LWWRegister<TValue>>.IDeltaOperation Delta =>
406-
new LWWDictionaryDelta(Underlying.Delta);
405+
public ORDictionary<TKey, LWWRegister<TValue>>.IDeltaOperation Delta =>
406+
Underlying.Delta == null ? null : new LWWDictionaryDelta(Underlying.Delta);
407407

408408
IReplicatedDelta IDeltaReplicatedData.Delta => Delta;
409409

0 commit comments

Comments
 (0)