Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4.1.0
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v3.2.0
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
dotnet-version: 8.0.x
- name: Install dependencies
run: |
sudo apt-get update
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/nuget_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4.1.0
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v3.2.0
uses: actions/setup-dotnet@v4
with:
dotnet-version: 6.0.x
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand Down
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -612,21 +612,17 @@ By default sync servers are obtained from `SyncServers` array and filtered by na
- `MaxErrors` maximum number of errors in `Interval` by instance
- `SwitchOffTime` time of synchronization switch off

Also you must add sync endpoints in `Configure` method of your `Startup` class.
Also you must add memcached endpoints in `Configure` method of your `Startup` class.

```c#
app.UseEndpoints(endpoints =>
{
endpoints.AddMemcachedSyncEndpoint<string>(this.Configuration);
endpoints.AddMemcachedSyncEndpoint<ComplexModel>(this.Configuration);
endpoints.AddMemcachedEndpoints(this.Configuration);
endpoints.MapControllers();
});
```

`Configuration` argument here is a property on a `Startup` instance
`AddMemcachedSyncEndpoint` - to store data. The generic parameter type must be the same as in corresponding `GetAsync` / `MultiGetAsync` / `StoreAsync` / `MultiStoreAsync` method calls.
`AddMemcachedEndpoints` - for delete and flush endpoints

When using cache synchronization feature, the `MemcachedClientResult.SyncSuccess` property can be inspected to determine whether the sync operation succeeded. When cache synchronization is not used this property is set to `false`.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

Expand Down
8 changes: 8 additions & 0 deletions samples/Aer.Memcached.Samples.Shared/ComplexModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,12 @@
public class ComplexModel
{
public Dictionary<string, long> TestValues { get; set; }

public SomeEnum SomeEnum { get; set; }
}

public enum SomeEnum
{
FirstValue,
SecondValue
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

Expand Down
2 changes: 0 additions & 2 deletions samples/Aer.Memcached.Samples.WebApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ public static void Main(string[] args)

app.UseEndpoints(endpoints =>
{
endpoints.AddMemcachedSyncEndpoint<string>(builder.Configuration);
endpoints.AddMemcachedSyncEndpoint<ComplexModel>(builder.Configuration);
endpoints.AddMemcachedEndpoints(builder.Configuration);
endpoints.MapControllers();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
"MaxErrors": 3,
"SwitchOffTime": "00:00:01"
}
},
"HashRing": {
"NumberOfVirtualNodes": 512,
"MaxDegreeOfParallelismForGettingNodes": 2
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

Expand Down
4 changes: 0 additions & 4 deletions samples/Aer.Memcached.Samples.WepApiToSync/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ public static void Main(string[] args)

app.UseEndpoints(endpoints =>
{
endpoints.AddMemcachedSyncEndpoint<string>(builder.Configuration);
endpoints.AddMemcachedSyncEndpoint<ComplexModel>(builder.Configuration);
endpoints.AddMemcachedSyncEndpoint<List<string>>(builder.Configuration);
endpoints.AddMemcachedSyncEndpoint<Dictionary<string, string>>(builder.Configuration);
endpoints.AddMemcachedEndpoints(builder.Configuration);
endpoints.MapControllers();
});
Expand Down
3 changes: 2 additions & 1 deletion src/Aer.ConsistentHash/Aer.ConsistentHash.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand All @@ -16,6 +16,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.1" />
<PackageReference Include="Standart.Hash.xxHash" Version="4.0.5" />
</ItemGroup>

Expand Down
19 changes: 19 additions & 0 deletions src/Aer.ConsistentHash/Config/HashRingSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Aer.ConsistentHash.Config;

public class HashRingSettings
{
/// <summary>
/// Default number of virtual nodes for HashRing
/// </summary>
public static readonly int DefaultNumberOfVirtualNodes = 256;

/// <summary>
/// Number of virtual nodes for HashRing
/// </summary>
public int NumberOfVirtualNodes { get; set; } = DefaultNumberOfVirtualNodes;

/// <summary>
/// Degree of parallelism while getting nodes
/// </summary>
public int? MaxDegreeOfParallelismForGettingNodes { get; set; }
}
38 changes: 25 additions & 13 deletions src/Aer.ConsistentHash/HashRing.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Collections.Concurrent;
using Aer.ConsistentHash.Abstractions;
using Aer.ConsistentHash.Config;
using Aer.ConsistentHash.Infrastructure;
using Microsoft.Extensions.Options;

namespace Aer.ConsistentHash;

Expand All @@ -15,24 +17,32 @@ public class HashRing<TNode> : INodeLocator<TNode>

private readonly IHashCalculator _hashCalculator;
private readonly int _numberOfVirtualNodes;
private readonly int _maxDegreeOfParallelismForGettingNodes;

private readonly ConcurrentDictionary<ulong, TNode> _hashToNodeMap = new();
private readonly ConcurrentDictionary<ulong, ulong[]> _nodeHashToVirtualNodeHashesMap = new();

// since we are only writing to this collection from inside the writer lock,
// we can safely use with non-thread-safe version
private readonly HashSet<TNode> _deadNodes = new();

private ulong[] _sortedNodeHashKeys;

/// <param name="hashCalculator">Calculates hash for nodes and keys</param>
/// <param name="numberOfVirtualNodes">Number of virtual nodes by one physical node</param>
public HashRing(IHashCalculator hashCalculator, int numberOfVirtualNodes = 256)
/// <param name="settings">HashRing settings</param>
public HashRing(IHashCalculator hashCalculator, IOptions<HashRingSettings> settings = null)
{
_locker = new ReaderWriterLockSlim();

_hashCalculator = hashCalculator;
_numberOfVirtualNodes = numberOfVirtualNodes;
_numberOfVirtualNodes = settings?.Value == null
? HashRingSettings.DefaultNumberOfVirtualNodes
: settings.Value.NumberOfVirtualNodes == 0
? HashRingSettings.DefaultNumberOfVirtualNodes
: settings.Value.NumberOfVirtualNodes;
_maxDegreeOfParallelismForGettingNodes = settings?.Value == null
? Environment.ProcessorCount
: settings.Value.MaxDegreeOfParallelismForGettingNodes ?? Environment.ProcessorCount;
}

public TNode GetNode(string key)
Expand All @@ -48,6 +58,7 @@ public TNode GetNode(string key)
}

var node = GetNodeInternal(key);

return node;
}
finally
Expand Down Expand Up @@ -75,7 +86,7 @@ public IDictionary<ReplicatedNode<TNode>, ConcurrentBag<string>> GetNodes(

Parallel.ForEach(
keys,
new ParallelOptions {MaxDegreeOfParallelism = Environment.ProcessorCount},
new ParallelOptions { MaxDegreeOfParallelism = _maxDegreeOfParallelismForGettingNodes },
key =>
{
var replicatedNode = GetReplicatedNodeInternal(key, replicationFactor);
Expand Down Expand Up @@ -137,7 +148,7 @@ public void AddNodes(IEnumerable<TNode> nodes)

public void AddNodes(params TNode[] nodes)
{
AddNodes((IEnumerable<TNode>) nodes);
AddNodes((IEnumerable<TNode>)nodes);
}

public void RemoveNode(TNode node)
Expand Down Expand Up @@ -202,22 +213,22 @@ public void MarkNodeDead(TNode node)
_locker.ExitWriteLock();
}
}

public IReadOnlyCollection<TNode> DrainDeadNodes()
{
try
{
_locker.EnterWriteLock();

// return defensive copy
var currentDeadNodes = _deadNodes.ToArray();

_deadNodes.Clear();

return currentDeadNodes;
}
finally
{
{
_locker.ExitWriteLock();
}
}
Expand Down Expand Up @@ -249,7 +260,7 @@ private ReplicatedNode<TNode> GetReplicatedNodeInternal(string key, uint replica
// means that replication factor is greater than total nodes count
// return all other nodes as replicas

var replicaNodes = _hashToNodeMap.Values.Except(new[] {primaryNode});
var replicaNodes = _hashToNodeMap.Values.Except(new[] { primaryNode });

foreach (var replicaNode in replicaNodes)
{
Expand All @@ -270,6 +281,7 @@ private ReplicatedNode<TNode> GetReplicatedNodeInternal(string key, uint replica
{
// this is our primary node - start getting replica nodes from this one
startingNodeFound = true;

continue;
}

Expand Down Expand Up @@ -365,7 +377,7 @@ private void AddNodeToCollections(TNode node)

_hashToNodeMap.GetOrAdd(nodeHash, node);
_nodeHashToVirtualNodeHashesMap.GetOrAdd(nodeHash, virtualNodeHashes);

foreach (var virtualNodeHash in virtualNodeHashes)
{
_hashToNodeMap.GetOrAdd(virtualNodeHash, node);
Expand Down
6 changes: 3 additions & 3 deletions src/Aer.Memcached.Client/Aer.Memcached.Client.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
Expand All @@ -18,8 +18,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MessagePack" Version="2.5.140" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="MessagePack" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.1" />
<PackageReference Include="morelinq" Version="3.3.2" />
<PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.2" />
<PackageReference Include="Polly" Version="8.1.0" />
Expand Down
8 changes: 4 additions & 4 deletions src/Aer.Memcached.Client/CacheSync/CacheSyncClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ public CacheSyncClient(
}

/// <inheritdoc />
public async Task SyncAsync<T>(
public async Task SyncAsync(
MemcachedConfiguration.SyncServer syncServer,
CacheSyncModel<T> data,
CacheSyncModel data,
CancellationToken token)
{
try
Expand All @@ -58,7 +58,7 @@ public async Task SyncAsync<T>(
MediaTypeNames.Application.Json);

var baseUri = new Uri(syncServer.Address);
var endpointUri = new Uri(baseUri, _config.SyncSettings.SyncEndpoint + TypeExtensions.GetTypeName<T>());
var endpointUri = new Uri(baseUri, _config.SyncSettings.SyncEndpoint + TypeExtensions.GetTypeName<byte>());

await RequestAsync(content, endpointUri, token);
}
Expand All @@ -69,7 +69,7 @@ public async Task SyncAsync<T>(
throw;
}
}

/// <inheritdoc />
public async Task DeleteAsync(
MemcachedConfiguration.SyncServer syncServer,
Expand Down
Loading
Loading