diff --git a/.github/actions/install/action.yml b/.github/actions/install/action.yml index 16a73c323..614811d95 100644 --- a/.github/actions/install/action.yml +++ b/.github/actions/install/action.yml @@ -59,7 +59,7 @@ runs: - name: Install .NET #on Windows - #if: inputs.dotnet == 'true' && inputs.os == 'windows-latest' + if: inputs.dotnet == 'true' # && inputs.os == 'windows-latest' uses: actions/setup-dotnet@v5 with: dotnet-version: | diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 9ac1f4699..681585ade 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -285,15 +285,7 @@ jobs: needs: [ analyze, build-release ] if: needs.analyze.outputs.todo == 'release' - steps: - - # Install our environment - - name: Install environment - uses: ./.github/actions/install - with: - os: ubuntu-latest - dotnet: true - java: false + steps: # checkout the hazelcast/hazelcast-csharp-client repository - name: Checkout code diff --git a/.github/workflows/trigger-release.yml b/.github/workflows/trigger-release.yml index 4ef447ca7..d0fb8ff9f 100644 --- a/.github/workflows/trigger-release.yml +++ b/.github/workflows/trigger-release.yml @@ -56,6 +56,11 @@ jobs: # finalize + trigger the release - name: Trigger Release if: ${{ github.event.inputs.dryrun == 'false' }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_OWNER: hazelcast + GH_REPO: hazelcast-csharp-client + GH_BRANCH: release/${{ github.ref_name }} shell: pwsh run: | ./hz.ps1 -noRestore -localRestore update-doc-version diff --git a/doc/latest-version b/doc/latest-version index 1e20ec35c..4cc0e35cb 100644 --- a/doc/latest-version +++ b/doc/latest-version @@ -1 +1 @@ -5.4.0 \ No newline at end of file +5.6.0 \ No newline at end of file diff --git a/doc/versions.md b/doc/versions.md index 85c2d6871..f118930cc 100644 --- a/doc/versions.md +++ b/doc/versions.md @@ -6,7 +6,7 @@ Versions lifecycle and support period follows the Hazelcast [Version Support Win ### Current Version -* 5.5.0 [general documentation](xref:doc-index-5-4-0) and [API reference](xref:api-index-5-4-0) +* 5.6.0 [general documentation](xref:doc-index-5-6-0) and [API reference](xref:api-index-5-6-0) ### Preview diff --git a/doc/xrefmap.yml b/doc/xrefmap.yml index 6fa3a8881..e77fa4809 100644 --- a/doc/xrefmap.yml +++ b/doc/xrefmap.yml @@ -88,3 +88,8 @@ references: href: 5.4.0/doc/index.html - uid: api-index-5-4-0 href: 5.4.0/api/index.html +- uid: doc-index-5-6-0 + name: 5.6.0 + href: 5.6.0/doc/index.html +- uid: api-index-5-6-0 + href: 5.6.0/api/index.html diff --git a/src/Directory.Build.props b/src/Directory.Build.props index cc1e7da32..5dd94b394 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -83,9 +83,9 @@ 4.0.0-alpha.2 --> - 5.5.0 - 5.5.0 - 5.5.0 + 5.6.0 + 5.6.0 + 5.6.0 diff --git a/src/Hazelcast.Net.Examples/Hazelcast.Net.Examples.csproj b/src/Hazelcast.Net.Examples/Hazelcast.Net.Examples.csproj index 9634dd525..de03dfd2d 100644 --- a/src/Hazelcast.Net.Examples/Hazelcast.Net.Examples.csproj +++ b/src/Hazelcast.Net.Examples/Hazelcast.Net.Examples.csproj @@ -8,6 +8,11 @@ net8.0;net9.0;net10.0 + + + $(NoWarn);CS1591 + + Exe Hazelcast.Examples @@ -35,21 +40,4 @@ - - - - - - - - - - SystemLinqAsyncEnumerable - - - - - diff --git a/src/Hazelcast.Net.Linq.Async/Hazelcast.Net.Linq.Async.csproj b/src/Hazelcast.Net.Linq.Async/Hazelcast.Net.Linq.Async.csproj index a520abe9a..4b0d1e856 100644 --- a/src/Hazelcast.Net.Linq.Async/Hazelcast.Net.Linq.Async.csproj +++ b/src/Hazelcast.Net.Linq.Async/Hazelcast.Net.Linq.Async.csproj @@ -51,13 +51,10 @@ - - - + + - + - - + diff --git a/src/Hazelcast.Net.Testing/Hazelcast.Net.Testing.csproj b/src/Hazelcast.Net.Testing/Hazelcast.Net.Testing.csproj index 6e16de9b5..aa2400546 100644 --- a/src/Hazelcast.Net.Testing/Hazelcast.Net.Testing.csproj +++ b/src/Hazelcast.Net.Testing/Hazelcast.Net.Testing.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netstandard2.1 + netstandard2.0;netstandard2.1; @@ -33,10 +33,21 @@ - - + + + + + + + + + MicrosoftBclMemory + + + + diff --git a/src/Hazelcast.Net.Testing/TestData/EmployeeTestObject.cs b/src/Hazelcast.Net.Testing/TestData/EmployeeTestObject.cs index c8496d7cd..a6722aa34 100644 --- a/src/Hazelcast.Net.Testing/TestData/EmployeeTestObject.cs +++ b/src/Hazelcast.Net.Testing/TestData/EmployeeTestObject.cs @@ -33,5 +33,18 @@ public DateTime Started } public long StartedAtTimeStamp { get; set; } public char Type { get; set; } + + public override bool Equals(object obj) + { + if (this == obj) return true; + if (obj == null || GetType() != obj.GetType()) return false; + var that = (EmployeeTestObject)obj; + return Id == that.Id && + Name == that.Name && + Salary.Equals(that.Salary) && + StartedAtTimeStamp == that.StartedAtTimeStamp && + Type == that.Type; + } + } } diff --git a/src/Hazelcast.Net.Tests/CP/CPRoutingTests.cs b/src/Hazelcast.Net.Tests/CP/CPRoutingTests.cs index 331bcdff8..1a0997202 100644 --- a/src/Hazelcast.Net.Tests/CP/CPRoutingTests.cs +++ b/src/Hazelcast.Net.Tests/CP/CPRoutingTests.cs @@ -26,7 +26,7 @@ namespace Hazelcast.Tests.CP { [Category("enterprise")] - [ServerCondition("[5.5)")] + [ServerCondition("[5.5,)")] [Timeout(30_000)] public class CPRoutingTests : MultiMembersRemoteTestBase { diff --git a/src/Hazelcast.Net.Tests/Clustering/ConnectMembersTests.cs b/src/Hazelcast.Net.Tests/Clustering/ConnectMembersTests.cs index e74e83113..398e96bdd 100644 --- a/src/Hazelcast.Net.Tests/Clustering/ConnectMembersTests.cs +++ b/src/Hazelcast.Net.Tests/Clustering/ConnectMembersTests.cs @@ -217,7 +217,7 @@ async Task ConnectMembers(MemberConnectionQueue memberConnectionQueue, Cancellat { await foreach (var request in memberConnectionQueue.WithCancellation(cancellationToken)) { - dequeuedRequests++; + Interlocked.Increment(ref dequeuedRequests); if (!memberCount.TryGetValue(request.Member.Id, out var count)) count = 0; memberCount[request.Member.Id] = ++count; logger.LogDebug($"Connect request={dequeuedRequests} member={request.Member.Id.ToShortString()} count={count} result={(count == successCount ? "success" : "failed")}"); @@ -246,9 +246,12 @@ await AssertEx.SucceedsEventually(() => cancellation.Cancel(); await connecting.CfAwaitCanceled(); - // each member retried twice = twice the 1s delay = 2s + // each member retried twice = twice the ~1s delay ~ 2s // we should not have completed faster than that, even so the code runs fully in-memory - Assert.That(elapsed, Is.GreaterThanOrEqualTo(TimeSpan.FromSeconds(2))); + // Note: The queue has 10ms error margin per wait, so we take 20ms total margin for two unsuccessful tries + var failedCount = successCount - 1; + var expectedMinTotalDelay = TimeSpan.FromMilliseconds(failedCount * 990); // 990ms per failed try including 10ms margin + Assert.That(elapsed, Is.GreaterThanOrEqualTo(expectedMinTotalDelay)); HConsole.WriteLine(this, $"Elapsed: {elapsed}"); Assert.That(queue.Count, Is.EqualTo(0)); diff --git a/src/Hazelcast.Net.Tests/Hazelcast.Net.Tests.csproj b/src/Hazelcast.Net.Tests/Hazelcast.Net.Tests.csproj index 5911df0af..8142d84e8 100644 --- a/src/Hazelcast.Net.Tests/Hazelcast.Net.Tests.csproj +++ b/src/Hazelcast.Net.Tests/Hazelcast.Net.Tests.csproj @@ -49,21 +49,9 @@ - - - + + - - - - - SystemLinqAsyncEnumerable - - - - diff --git a/src/Hazelcast.Net.Tests/Remote/UnisocketTests.cs b/src/Hazelcast.Net.Tests/Remote/UnisocketTests.cs index 5387c44e4..70dc4d469 100644 --- a/src/Hazelcast.Net.Tests/Remote/UnisocketTests.cs +++ b/src/Hazelcast.Net.Tests/Remote/UnisocketTests.cs @@ -126,7 +126,7 @@ public async Task TestClientConnectsToOneMember() [Test] - [ServerCondition("[6.0)")] + [ServerCondition("5.6.0")] public async Task TestRoutingModesInOrderWithServer() { var script = @"result = com.hazelcast.client.config.RoutingMode.SINGLE_MEMBER.getId() + @@ -147,7 +147,7 @@ public async Task TestRoutingModesInOrderWithServer() } [Test] - [ServerCondition("[5.5.0,6.0)")] + [ServerCondition("[5.5,)")] public async Task TestRoutingModesInOrderWithServerWithPreviousPackage() { var script = @"result = com.hazelcast.client.impl.connection.tcp.RoutingMode.SINGLE_MEMBER.getId() + diff --git a/src/Hazelcast.Net.Tests/Vector/VectorCollectionTests.cs b/src/Hazelcast.Net.Tests/Vector/VectorCollectionTests.cs index 04e726514..d101ef05b 100644 --- a/src/Hazelcast.Net.Tests/Vector/VectorCollectionTests.cs +++ b/src/Hazelcast.Net.Tests/Vector/VectorCollectionTests.cs @@ -21,7 +21,7 @@ using NUnit.Framework; namespace Hazelcast.Tests.Vector { - [ServerCondition("[5.5.0")] + [ServerCondition("5.5")] [Category("enterprise")] public class VectorCollectionTests : SingleMemberClientRemoteTestBase { diff --git a/src/Hazelcast.Net/Clustering/ClusterEvents.cs b/src/Hazelcast.Net/Clustering/ClusterEvents.cs index 8e6499e97..503821119 100644 --- a/src/Hazelcast.Net/Clustering/ClusterEvents.cs +++ b/src/Hazelcast.Net/Clustering/ClusterEvents.cs @@ -343,7 +343,7 @@ private async Task AddSubscriptionsAsync(MemberConnection connection, IReadOnlyC } // we are done now - lock (_subscribeTasksMutex) _subscribeTasks.Remove(connection); + lock (_subscribeTasksMutex) _subscribeTasks?.Remove(connection); } /// diff --git a/src/Hazelcast.Net/DistributedObjects/Impl/HVectorCollection.cs b/src/Hazelcast.Net/DistributedObjects/Impl/HVectorCollection.cs index 6c6352e69..5eb1a479c 100644 --- a/src/Hazelcast.Net/DistributedObjects/Impl/HVectorCollection.cs +++ b/src/Hazelcast.Net/DistributedObjects/Impl/HVectorCollection.cs @@ -17,6 +17,7 @@ using System.Threading.Tasks; using Hazelcast.Clustering; using Hazelcast.Core; +using Hazelcast.Messaging; using Hazelcast.Models; using Hazelcast.Protocol.Codecs; using Hazelcast.Serialization; @@ -67,22 +68,39 @@ public async Task> PutIfAbsentAsync(TKey key, IVectorDocum var rawResponse = VectorCollectionPutIfAbsentCodec.DecodeResponse(response).Value; return await DeserializeIVectorDocumentAsync(rawResponse).CfAwait(); } - public Task PutAllAsync([NotNull] IDictionary> vectorDocumentMap) + public async Task PutAllAsync([NotNull] IDictionary> vectorDocumentMap) { vectorDocumentMap.ThrowIfNull(); + var entriesByPartition = new Dictionary>>>(); - var rawEntries = new List>>(); foreach (var kvp in vectorDocumentMap) { var key = kvp.Key ?? throw new ArgumentException($"Key cannot be null in {nameof(vectorDocumentMap)}."); var val = kvp.Value ?? throw new ArgumentException($"Value cannot be null in {nameof(vectorDocumentMap)}."); - var dataKey = ToSafeData(key); - var dataValue = ToSafeData(val); - var rawDocument = new VectorDocument(dataValue, kvp.Value.Vectors); - rawEntries.Add(new KeyValuePair>(dataKey, rawDocument)); + var(dataKey, rawDocument) = PrepareForPut(key, val); + var partitionId = Cluster.Partitioner.GetPartitionId(dataKey.PartitionHash); + + if (entriesByPartition.TryGetValue(partitionId, out var list)) + { + list.Add(new KeyValuePair>(dataKey, rawDocument)); + } + else + { + entriesByPartition[partitionId] = new List>>() + { + new KeyValuePair>(dataKey, rawDocument) + }; + } } - var message = VectorCollectionPutAllCodec.EncodeRequest(Name, rawEntries); - return Cluster.Messaging.SendAsync(message); + + var tasks = new List>(); + foreach (var entry in entriesByPartition) + { + var message = VectorCollectionPutAllCodec.EncodeRequest(Name, entry.Value); + tasks.Add(Cluster.Messaging.SendToPartitionOwnerAsync(message, entry.Key)); + } + + await Task.WhenAll(tasks).CfAwait(); } public async Task> RemoveAsync(TKey key) { @@ -143,7 +161,7 @@ private async Task> DeserializeIVectorDocumentAsync( { return null; } - + var userObject = await ToObjectAsync(rawResponse.Value).CfAwait(); return new VectorDocument(userObject, rawResponse.Vectors); } diff --git a/src/Hazelcast.Net/Hazelcast.Net.csproj b/src/Hazelcast.Net/Hazelcast.Net.csproj index 75d5a0582..f7a7eb4a3 100644 --- a/src/Hazelcast.Net/Hazelcast.Net.csproj +++ b/src/Hazelcast.Net/Hazelcast.Net.csproj @@ -68,7 +68,7 @@ - + diff --git a/src/Hazelcast.Net/Models/SingleVectorValues.cs b/src/Hazelcast.Net/Models/SingleVectorValues.cs index bbd132cb8..882e6503c 100644 --- a/src/Hazelcast.Net/Models/SingleVectorValues.cs +++ b/src/Hazelcast.Net/Models/SingleVectorValues.cs @@ -11,7 +11,7 @@ // 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. -using Hazelcast.Serialization; +using System.Linq; namespace Hazelcast.Models { public class SingleVectorValues : VectorValues @@ -22,12 +22,22 @@ internal SingleVectorValues(float[] vector) Vector = vector; } public float[] Vector { get; } - + + + /// public override string ToString() { var val = Vector == null ? "null" : $"[{string.Join(", ", Vector)}]"; return $"SingleVectorValues{{vector={val}}}"; } + /// + public override bool Equals(object obj) + { + if (this == obj) return true; + if (obj == null || GetType() != obj.GetType()) return false; + var that = (SingleVectorValues)obj; + return Vector.SequenceEqual(that.Vector); + } } } diff --git a/src/Hazelcast.Net/Models/VectorSearchOptions.cs b/src/Hazelcast.Net/Models/VectorSearchOptions.cs index 9ede16314..4d73e1582 100644 --- a/src/Hazelcast.Net/Models/VectorSearchOptions.cs +++ b/src/Hazelcast.Net/Models/VectorSearchOptions.cs @@ -34,7 +34,7 @@ public VectorSearchOptions(bool includeValue = default, IncludeValue = includeValue; IncludeVectors = includeVectors; Limit = limit; - Hints = hints; + Hints = hints ?? new Dictionary(); } /// diff --git a/src/Hazelcast.Net/PublicAPI/PublicAPI.Shipped.txt b/src/Hazelcast.Net/PublicAPI/PublicAPI.Shipped.txt index 931c268de..06c38c3c2 100644 --- a/src/Hazelcast.Net/PublicAPI/PublicAPI.Shipped.txt +++ b/src/Hazelcast.Net/PublicAPI/PublicAPI.Shipped.txt @@ -2653,4 +2653,5 @@ override Hazelcast.Models.ClusterVersion.GetHashCode() -> int ~Hazelcast.Networking.NetworkingOptions.RoutingMode.get -> Hazelcast.Networking.RoutingMode ~override Hazelcast.Models.ClusterVersion.Equals(object obj) -> bool ~override Hazelcast.Models.ClusterVersion.ToString() -> string -~static Hazelcast.Models.ClusterVersion.Parse(string value) -> Hazelcast.Models.ClusterVersion \ No newline at end of file +~static Hazelcast.Models.ClusterVersion.Parse(string value) -> Hazelcast.Models.ClusterVersion +~override Hazelcast.Models.SingleVectorValues.Equals(object obj) -> bool \ No newline at end of file diff --git a/src/Hazelcast.Net/PublicAPI/net10.0/PublicAPI.Shipped.txt b/src/Hazelcast.Net/PublicAPI/net10.0/PublicAPI.Shipped.txt index 12001093c..4daf08313 100644 --- a/src/Hazelcast.Net/PublicAPI/net10.0/PublicAPI.Shipped.txt +++ b/src/Hazelcast.Net/PublicAPI/net10.0/PublicAPI.Shipped.txt @@ -2711,3 +2711,4 @@ virtual Hazelcast.Serialization.Compact.CompactSerializerBase.TypeName.get -> ~virtual Hazelcast.Query.PredicateBuilder.IsLike(string pattern) -> Hazelcast.Query.IPredicate ~virtual Hazelcast.Query.PredicateBuilder.IsNotEqualTo(object value) -> Hazelcast.Query.IPredicate ~virtual Hazelcast.Query.PredicateBuilder.Matches(string regex) -> Hazelcast.Query.IPredicate +~override Hazelcast.Models.SingleVectorValues.Equals(object obj) -> bool \ No newline at end of file diff --git a/src/Hazelcast.Net/PublicAPI/net8.0/PublicAPI.Shipped.txt b/src/Hazelcast.Net/PublicAPI/net8.0/PublicAPI.Shipped.txt index 12001093c..4daf08313 100644 --- a/src/Hazelcast.Net/PublicAPI/net8.0/PublicAPI.Shipped.txt +++ b/src/Hazelcast.Net/PublicAPI/net8.0/PublicAPI.Shipped.txt @@ -2711,3 +2711,4 @@ virtual Hazelcast.Serialization.Compact.CompactSerializerBase.TypeName.get -> ~virtual Hazelcast.Query.PredicateBuilder.IsLike(string pattern) -> Hazelcast.Query.IPredicate ~virtual Hazelcast.Query.PredicateBuilder.IsNotEqualTo(object value) -> Hazelcast.Query.IPredicate ~virtual Hazelcast.Query.PredicateBuilder.Matches(string regex) -> Hazelcast.Query.IPredicate +~override Hazelcast.Models.SingleVectorValues.Equals(object obj) -> bool \ No newline at end of file diff --git a/src/Hazelcast.Net/PublicAPI/net9.0/PublicAPI.Shipped.txt b/src/Hazelcast.Net/PublicAPI/net9.0/PublicAPI.Shipped.txt index 12001093c..4daf08313 100644 --- a/src/Hazelcast.Net/PublicAPI/net9.0/PublicAPI.Shipped.txt +++ b/src/Hazelcast.Net/PublicAPI/net9.0/PublicAPI.Shipped.txt @@ -2711,3 +2711,4 @@ virtual Hazelcast.Serialization.Compact.CompactSerializerBase.TypeName.get -> ~virtual Hazelcast.Query.PredicateBuilder.IsLike(string pattern) -> Hazelcast.Query.IPredicate ~virtual Hazelcast.Query.PredicateBuilder.IsNotEqualTo(object value) -> Hazelcast.Query.IPredicate ~virtual Hazelcast.Query.PredicateBuilder.Matches(string regex) -> Hazelcast.Query.IPredicate +~override Hazelcast.Models.SingleVectorValues.Equals(object obj) -> bool \ No newline at end of file diff --git a/src/Hazelcast.Net/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt b/src/Hazelcast.Net/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt index 776f4142a..1cd904f7b 100644 --- a/src/Hazelcast.Net/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt +++ b/src/Hazelcast.Net/PublicAPI/netstandard2.0/PublicAPI.Shipped.txt @@ -2711,5 +2711,5 @@ virtual Hazelcast.Serialization.Compact.CompactSerializerBase.TypeName.get -> ~virtual Hazelcast.Query.PredicateBuilder.IsLike(string pattern) -> Hazelcast.Query.IPredicate ~virtual Hazelcast.Query.PredicateBuilder.IsNotEqualTo(object value) -> Hazelcast.Query.IPredicate ~virtual Hazelcast.Query.PredicateBuilder.Matches(string regex) -> Hazelcast.Query.IPredicate - +~override Hazelcast.Models.SingleVectorValues.Equals(object obj) -> bool diff --git a/src/Hazelcast.Net/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt b/src/Hazelcast.Net/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt index b24f79f48..7a20fbc77 100644 --- a/src/Hazelcast.Net/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt +++ b/src/Hazelcast.Net/PublicAPI/netstandard2.1/PublicAPI.Shipped.txt @@ -2711,5 +2711,5 @@ virtual Hazelcast.Serialization.Compact.CompactSerializerBase.TypeName.get -> ~virtual Hazelcast.Query.PredicateBuilder.IsLike(string pattern) -> Hazelcast.Query.IPredicate ~virtual Hazelcast.Query.PredicateBuilder.IsNotEqualTo(object value) -> Hazelcast.Query.IPredicate ~virtual Hazelcast.Query.PredicateBuilder.Matches(string regex) -> Hazelcast.Query.IPredicate - +~override Hazelcast.Models.SingleVectorValues.Equals(object obj) -> bool