Browse Source

Use deterministic hash code.

pull/1081/head
Sebastian Stehle 2 years ago
parent
commit
e5a681156c
  1. 28
      backend/src/Squidex.Infrastructure/DomainId.cs
  2. 13
      backend/src/Squidex.Infrastructure/IDeterministicHashCode.cs
  3. 2
      backend/src/Squidex.Infrastructure/States/ShardedService.cs
  4. 8
      backend/src/Squidex.Infrastructure/States/Sharding.cs
  5. 10
      backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs
  6. 2
      backend/tests/Squidex.Infrastructure.Tests/DomainIdTests.cs
  7. 8
      backend/tests/Squidex.Infrastructure.Tests/States/ShardedServiceTests.cs
  8. 4
      backend/tests/Squidex.Infrastructure.Tests/States/ShardingTests.cs
  9. 2
      frontend/src/app/shell/pages/internal/notification-dropdown.component.scss

28
backend/src/Squidex.Infrastructure/DomainId.cs

@ -10,7 +10,7 @@ using System.ComponentModel;
namespace Squidex.Infrastructure;
[TypeConverter(typeof(DomainIdTypeConverter))]
public readonly struct DomainId : IEquatable<DomainId>, IComparable<DomainId>
public readonly struct DomainId : IEquatable<DomainId>, IComparable<DomainId>, IDeterministicHashCode
{
private static readonly string EmptyString = Guid.Empty.ToString();
@ -74,6 +74,32 @@ public readonly struct DomainId : IEquatable<DomainId>, IComparable<DomainId>
return id ?? EmptyString;
}
public int GetDeterministicHashCode()
{
unchecked
{
int hash1 = (5381 << 16) + 5381;
int hash2 = hash1;
if (id != null)
{
for (int i = 0; i < id.Length; i += 2)
{
hash1 = ((hash1 << 5) + hash1) ^ id[i];
if (i == id.Length - 1)
{
break;
}
hash2 = ((hash2 << 5) + hash2) ^ id[i + 1];
}
}
return hash1 + (hash2 * 1566083941);
}
}
public int CompareTo(DomainId other)
{
return string.Compare(ToString(), other.ToString(), StringComparison.Ordinal);

13
backend/src/Squidex.Infrastructure/IDeterministicHashCode.cs

@ -0,0 +1,13 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Infrastructure;
public interface IDeterministicHashCode
{
int GetDeterministicHashCode();
}

2
backend/src/Squidex.Infrastructure/States/ShardedService.cs

@ -9,7 +9,7 @@ using Squidex.Hosting;
namespace Squidex.Infrastructure.States;
public abstract class ShardedService<TKey, TService> : IInitializable where TKey : notnull
public abstract class ShardedService<TKey, TService> : IInitializable where TKey : notnull, IDeterministicHashCode
{
private readonly Dictionary<string, TService> shards = [];
private readonly IShardingStrategy sharding;

8
backend/src/Squidex.Infrastructure/States/Sharding.cs

@ -11,7 +11,7 @@ namespace Squidex.Infrastructure.States;
public interface IShardingStrategy
{
string GetShardKey<T>(T key) where T : notnull;
string GetShardKey<T>(T key) where T : notnull, IDeterministicHashCode;
IEnumerable<string> GetShardKeys();
}
@ -24,7 +24,7 @@ public sealed class SingleSharding : IShardingStrategy
{
}
public string GetShardKey<T>(T key) where T : notnull
public string GetShardKey<T>(T key) where T : notnull, IDeterministicHashCode
{
return string.Empty;
}
@ -44,9 +44,9 @@ public sealed class PartitionedSharding : IShardingStrategy
this.numPartitions = numPartitions;
}
public string GetShardKey<T>(T key) where T : notnull
public string GetShardKey<T>(T key) where T : notnull, IDeterministicHashCode
{
var partition = Math.Abs(key.GetHashCode()) % numPartitions;
var partition = Math.Abs(key.GetDeterministicHashCode()) % numPartitions;
return GetShardKey(partition);
}

10
backend/tests/Squidex.Domain.Apps.Entities.Tests/AppProviderExtensionsTests.cs

@ -9,6 +9,7 @@ using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.TestHelpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities;
@ -17,6 +18,15 @@ public class AppProviderExtensionsTests : GivenContext
private readonly NamedId<DomainId> componentId1 = NamedId.Of(DomainId.NewGuid(), "my-schema");
private readonly NamedId<DomainId> componentId2 = NamedId.Of(DomainId.NewGuid(), "my-schema");
[Fact]
public void X()
{
var y = DomainId.Create("c3750ec4-baf1-44af-85f0-1495ab4f9f1a");
var x = new PartitionedSharding(20).GetShardKey(y);
}
[Fact]
public async Task Should_do_nothing_if_no_component_found()
{

2
backend/tests/Squidex.Infrastructure.Tests/DomainIdTests.cs

@ -138,10 +138,12 @@ public class DomainIdTests
Assert.Equal(domainId_1_a, domainId_1_b);
Assert.Equal(domainId_1_a.GetHashCode(), domainId_1_b.GetHashCode());
Assert.Equal(domainId_1_a.GetDeterministicHashCode(), domainId_1_b.GetDeterministicHashCode());
Assert.True(domainId_1_a.Equals((object)domainId_1_b));
Assert.NotEqual(domainId_1_a, domainId_2_a);
Assert.NotEqual(domainId_1_a.GetHashCode(), domainId_2_a.GetHashCode());
Assert.NotEqual(domainId_1_a.GetDeterministicHashCode(), domainId_2_a.GetDeterministicHashCode());
Assert.False(domainId_1_a.Equals((object)domainId_2_a));
Assert.True(domainId_1_a == domainId_1_b);

8
backend/tests/Squidex.Infrastructure.Tests/States/ShardedServiceTests.cs

@ -19,14 +19,14 @@ public class ShardedServiceTests
{
}
private class TestSut : ShardedService<int, IInner>
private class TestSut : ShardedService<DomainId, IInner>
{
public TestSut(IShardingStrategy sharding, Func<string, IInner> factory)
: base(sharding, factory)
{
}
public IInner ExposeShard(int key)
public IInner ExposeShard(DomainId key)
{
return Shard(key);
}
@ -76,7 +76,7 @@ public class ShardedServiceTests
[Fact]
public void Should_provide_shards()
{
Assert.Equal(inner1, sut.ExposeShard(0));
Assert.Equal(inner2, sut.ExposeShard(1));
Assert.Equal(inner1, sut.ExposeShard(DomainId.Create("2")));
Assert.Equal(inner2, sut.ExposeShard(DomainId.Create("3")));
}
}

4
backend/tests/Squidex.Infrastructure.Tests/States/ShardingTests.cs

@ -16,7 +16,7 @@ public class ShardingTests
for (var i = 0; i < 1000; i++)
{
var shardKey = strategy.GetShardKey(Guid.NewGuid());
var shardKey = strategy.GetShardKey(DomainId.NewGuid());
Assert.Equal(string.Empty, shardKey);
}
@ -39,7 +39,7 @@ public class ShardingTests
for (var i = 0; i < 1000; i++)
{
var shardKey = strategy.GetShardKey(Guid.NewGuid());
var shardKey = strategy.GetShardKey(DomainId.NewGuid());
Assert.True(shardKey is "_0" or "_1" or "_2");
}

2
frontend/src/app/shell/pages/internal/notification-dropdown.component.scss

@ -14,6 +14,6 @@
}
.badge {
@include absolute(-.5rem, 0, null, null);
@include absolute(-.375rem, 0, null, null);
font-size: 80%;
}
Loading…
Cancel
Save