Skip to content

Commit 37b89fa

Browse files
authored
Merge pull request #24 from RayTale/develop
Improve storage scalability
2 parents 4af43d7 + b612755 commit 37b89fa

12 files changed

Lines changed: 138 additions & 64 deletions

src/Storage/Vertex.Storage.Linq2db/Core/EventStorageAttribute.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,42 @@
55
namespace Vertex.Storage.Linq2db.Core
66
{
77
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
8-
public class EventStorageAttribute : Attribute
8+
public class EventStorageAttribute : EventStorageBaseAttribute
99
{
10+
private readonly Func<string, string> shardingFunc;
11+
private readonly string optionName;
12+
1013
public EventStorageAttribute(string optionName, string name, int sharding = 0)
1114
{
1215
this.Name = name;
13-
this.OptionName = optionName;
16+
this.optionName = optionName;
1417
if (sharding < 0)
1518
{
1619
throw new ArgumentOutOfRangeException("sharding must be greater than 0");
1720
}
1821

1922
if (sharding == 0)
2023
{
21-
this.ShardingFunc = actorId => $"Vertex_Event_{name}".ToLower();
24+
this.shardingFunc = actorId => $"Vertex_Event_{name}".ToLower();
2225
}
2326
else
2427
{
2528
var tableNames = Enumerable.Range(0, sharding).Select(index => $"Vertex_Event_{name}_{index}".ToLower()).ToList();
2629
var hash = new ConsistentHash(tableNames, tableNames.Count * 10);
27-
this.ShardingFunc = actorId => hash.GetNode(actorId);
30+
this.shardingFunc = actorId => hash.GetNode(actorId);
2831
}
2932
}
3033

31-
public string OptionName { get; init; }
32-
3334
public string Name { get; init; }
3435

35-
public Func<string, string> ShardingFunc { get; init; }
36+
public override string GetTableName(string actorId)
37+
{
38+
return this.shardingFunc(actorId);
39+
}
40+
41+
public override string GetOptionName(string actorId)
42+
{
43+
return this.optionName;
44+
}
3645
}
3746
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace Vertex.Storage.Linq2db.Core
4+
{
5+
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
6+
public abstract class EventStorageBaseAttribute : Attribute
7+
{
8+
public abstract string GetTableName(string actorId);
9+
10+
public abstract string GetOptionName(string actorId);
11+
}
12+
}

src/Storage/Vertex.Storage.Linq2db/Core/SnapshotStorageAttribute.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,42 @@
55
namespace Vertex.Storage.Linq2db.Core
66
{
77
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
8-
public class SnapshotStorageAttribute : Attribute
8+
public class SnapshotStorageAttribute : SnapshotStorageBaseAttribute
99
{
10+
private readonly Func<string, string> shardingFunc;
11+
private readonly string optionName;
12+
1013
public SnapshotStorageAttribute(string optionName, string name, int sharding = 0)
1114
{
1215
this.Name = name;
13-
this.OptionName = optionName;
16+
this.optionName = optionName;
1417
if (sharding < 0)
1518
{
1619
throw new ArgumentOutOfRangeException("sharding must be greater than 0");
1720
}
1821

1922
if (sharding == 0)
2023
{
21-
this.ShardingFunc = actorId => $"Vertex_Snapshot_{name}".ToLower();
24+
this.shardingFunc = actorId => $"Vertex_Snapshot_{name}".ToLower();
2225
}
2326
else
2427
{
2528
var tableNames = Enumerable.Range(0, sharding).Select(index => $"Vertex_Snapshot_{name}_{index}".ToLower()).ToList();
2629
var hash = new ConsistentHash(tableNames, tableNames.Count * 10);
27-
this.ShardingFunc = actorId => hash.GetNode(actorId);
30+
this.shardingFunc = actorId => hash.GetNode(actorId);
2831
}
2932
}
3033

31-
public string OptionName { get; init; }
32-
3334
public string Name { get; init; }
3435

35-
public Func<string, string> ShardingFunc { get; init; }
36+
public override string GetOptionName(string actorId)
37+
{
38+
return this.optionName;
39+
}
40+
41+
public override string GetTableName(string actorId)
42+
{
43+
return this.shardingFunc(actorId);
44+
}
3645
}
3746
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace Vertex.Storage.Linq2db.Core
4+
{
5+
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
6+
public abstract class SnapshotStorageBaseAttribute : Attribute
7+
{
8+
public abstract string GetTableName(string actorId);
9+
10+
public abstract string GetOptionName(string actorId);
11+
}
12+
}

src/Storage/Vertex.Storage.Linq2db/Core/TxEventStorageAttribute.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,42 @@
55
namespace Vertex.Storage.Linq2db.Core
66
{
77
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
8-
public class TxEventStorageAttribute : Attribute
8+
public class TxEventStorageAttribute : TxEventStorageBaseAttribute
99
{
10+
private readonly Func<string, string> shardingFunc;
11+
private readonly string optionName;
12+
1013
public TxEventStorageAttribute(string optionName, string name, int sharding = 0)
1114
{
1215
this.Name = name;
13-
this.OptionName = optionName;
16+
this.optionName = optionName;
1417
if (sharding < 0)
1518
{
1619
throw new ArgumentOutOfRangeException("sharding must be greater than 0");
1720
}
1821

1922
if (sharding == 0)
2023
{
21-
this.ShardingFunc = actorId => $"Vertex_TxEvent_{name}".ToLower();
24+
this.shardingFunc = actorId => $"Vertex_TxEvent_{name}".ToLower();
2225
}
2326
else
2427
{
2528
var tableNames = Enumerable.Range(0, sharding).Select(index => $"Vertex_TxEvent_{name}_{index}".ToLower()).ToList();
2629
var hash = new ConsistentHash(tableNames, tableNames.Count * 10);
27-
this.ShardingFunc = actorId => hash.GetNode(actorId);
30+
this.shardingFunc = actorId => hash.GetNode(actorId);
2831
}
2932
}
3033

31-
public string OptionName { get; init; }
32-
3334
public string Name { get; init; }
3435

35-
public Func<string, string> ShardingFunc { get; init; }
36+
public override string GetOptionName(string actorId)
37+
{
38+
return this.optionName;
39+
}
40+
41+
public override string GetTableName(string actorId)
42+
{
43+
return this.shardingFunc(actorId);
44+
}
3645
}
3746
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace Vertex.Storage.Linq2db.Core
4+
{
5+
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
6+
public abstract class TxEventStorageBaseAttribute : Attribute
7+
{
8+
public abstract string GetTableName(string actorId);
9+
10+
public abstract string GetOptionName(string actorId);
11+
}
12+
}

src/Storage/Vertex.Storage.Linq2db/Storage/EventStorage.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using System.Runtime.CompilerServices;
54
using System.Threading.Tasks;
65
using LinqToDB;
76
using LinqToDB.Data;

src/Storage/Vertex.Storage.Linq2db/Storage/Factory/EventStorageFactory.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ namespace Vertex.Storage.Linq2db.Storage
1616
{
1717
public class EventStorageFactory : IEventStorageFactory
1818
{
19-
private readonly ConcurrentDictionary<Type, EventStorageAttribute> typeAttributes = new ConcurrentDictionary<Type, EventStorageAttribute>();
20-
private readonly ConcurrentDictionary<string, Lazy<Task<object>>> eventStorageDict = new ConcurrentDictionary<string, Lazy<Task<object>>>();
19+
private readonly ConcurrentDictionary<Type, EventStorageBaseAttribute> typeAttributes = new();
20+
private readonly ConcurrentDictionary<string, Lazy<Task<object>>> eventStorageDict = new();
2121
private readonly DbFactory dbFactory;
2222
private readonly IGrainFactory grainFactory;
2323
private readonly IServiceProvider serviceProvider;
@@ -33,28 +33,31 @@ public async ValueTask<IEventStorage<TPrimaryKey>> Create<TPrimaryKey>(IActor<TP
3333
{
3434
var attribute = this.typeAttributes.GetOrAdd(actor.GetType(), key =>
3535
{
36-
var attributes = key.GetCustomAttributes(typeof(EventStorageAttribute), false);
37-
if (attributes.Length > 0)
36+
var attributes = key.GetCustomAttributes(false);
37+
var attribute = attributes.SingleOrDefault(att => typeof(EventStorageBaseAttribute).IsAssignableFrom(att.GetType()));
38+
if (attribute != default)
3839
{
39-
return attributes.First() as EventStorageAttribute;
40+
return attribute as EventStorageBaseAttribute;
4041
}
4142
else
4243
{
43-
throw new MissingAttributeException($"{nameof(EventStorageAttribute)}=>{key.Name}");
44+
throw new MissingAttributeException($"{nameof(EventStorageBaseAttribute)}=>{key.Name}");
4445
}
4546
});
46-
var tableName = attribute.ShardingFunc(actor.ActorId.ToString());
47-
var storage = await this.eventStorageDict.GetOrAdd($"{attribute.OptionName}_{tableName}", key =>
47+
var actorId = actor.ActorId.ToString();
48+
var tableName = attribute.GetTableName(actorId);
49+
var optionName = attribute.GetOptionName(actorId);
50+
var storage = await this.eventStorageDict.GetOrAdd($"{optionName}_{tableName}", key =>
4851
new Lazy<Task<object>>(async () =>
4952
{
50-
using var db = this.dbFactory.GetEventDb(attribute.OptionName);
53+
using var db = this.dbFactory.GetEventDb(optionName);
5154
await db.CreateTableIfNotExists<EventEntity<TPrimaryKey>>(this.grainFactory, key, tableName, async () =>
5255
{
5356
var indexGenerator = db.GetGenerator();
5457
await indexGenerator.CreateUniqueIndexIfNotExists(db, tableName, $"{tableName}_event_unique", nameof(EventEntity<TPrimaryKey>.ActorId).ToLower(), nameof(EventEntity<TPrimaryKey>.Version).ToLower());
5558
await indexGenerator.CreateUniqueIndexIfNotExists(db, tableName, $"{tableName}_event_flow_unique", nameof(EventEntity<TPrimaryKey>.ActorId).ToLower(), nameof(EventEntity<TPrimaryKey>.Name).ToLower(), nameof(EventEntity<TPrimaryKey>.FlowId).ToLower());
5659
});
57-
return new EventStorage<TPrimaryKey>(this.serviceProvider, this.dbFactory, attribute.OptionName, tableName);
60+
return new EventStorage<TPrimaryKey>(this.serviceProvider, this.dbFactory, optionName, tableName);
5861
})).Value;
5962
return storage as IEventStorage<TPrimaryKey>;
6063
}

src/Storage/Vertex.Storage.Linq2db/Storage/Factory/SnapshotStorageFactory.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ namespace Vertex.Storage.Linq2db.Storage
1414
{
1515
public class SnapshotStorageFactory : ISnapshotStorageFactory
1616
{
17-
private readonly ConcurrentDictionary<Type, SnapshotStorageAttribute> typeAttributes = new ConcurrentDictionary<Type, SnapshotStorageAttribute>();
18-
private readonly ConcurrentDictionary<string, Lazy<Task<object>>> eventStorageDict = new ConcurrentDictionary<string, Lazy<Task<object>>>();
17+
private readonly ConcurrentDictionary<Type, SnapshotStorageBaseAttribute> typeAttributes = new();
18+
private readonly ConcurrentDictionary<string, Lazy<Task<object>>> eventStorageDict = new();
1919
private readonly DbFactory dbFactory;
2020
private readonly IGrainFactory grainFactory;
2121
private readonly IServiceProvider serviceProvider;
@@ -31,23 +31,26 @@ public async ValueTask<ISnapshotStorage<TPrimaryKey>> Create<TPrimaryKey>(IActor
3131
{
3232
var attribute = this.typeAttributes.GetOrAdd(actor.GetType(), key =>
3333
{
34-
var attributes = key.GetCustomAttributes(typeof(SnapshotStorageAttribute), false);
35-
if (attributes.Length > 0)
34+
var attributes = key.GetCustomAttributes(false);
35+
var attribute = attributes.SingleOrDefault(att => typeof(SnapshotStorageBaseAttribute).IsAssignableFrom(att.GetType()));
36+
if (attribute != default)
3637
{
37-
return attributes.First() as SnapshotStorageAttribute;
38+
return attribute as SnapshotStorageBaseAttribute;
3839
}
3940
else
4041
{
41-
throw new MissingAttributeException($"{nameof(SnapshotStorageAttribute)}=>{key.Name}");
42+
throw new MissingAttributeException($"{nameof(SnapshotStorageBaseAttribute)}=>{key.Name}");
4243
}
4344
});
44-
var tableName = attribute.ShardingFunc(actor.ActorId.ToString());
45-
var storage = await this.eventStorageDict.GetOrAdd($"{attribute.OptionName}_{tableName}", key =>
45+
var actorId = actor.ActorId.ToString();
46+
var tableName = attribute.GetTableName(actorId);
47+
var optionName = attribute.GetOptionName(actorId);
48+
var storage = await this.eventStorageDict.GetOrAdd($"{optionName}_{tableName}", key =>
4649
new Lazy<Task<object>>(async () =>
4750
{
48-
using var db = this.dbFactory.GetEventDb(attribute.OptionName);
51+
using var db = this.dbFactory.GetEventDb(optionName);
4952
await db.CreateTableIfNotExists<SnapshotEntity<TPrimaryKey>>(this.grainFactory, key, tableName);
50-
return new SnapshotStorage<TPrimaryKey>(this.serviceProvider, this.dbFactory, attribute.OptionName, tableName);
53+
return new SnapshotStorage<TPrimaryKey>(this.serviceProvider, this.dbFactory, optionName, tableName);
5154
})).Value;
5255

5356
return storage as ISnapshotStorage<TPrimaryKey>;

src/Storage/Vertex.Storage.Linq2db/Storage/Factory/SubSnapshotStorageFactory.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ namespace Vertex.Storage.Linq2db.Storage
1414
{
1515
public class SubSnapshotStorageFactory : ISubSnapshotStorageFactory
1616
{
17-
private readonly ConcurrentDictionary<Type, SnapshotStorageAttribute> typeAttributes = new ConcurrentDictionary<Type, SnapshotStorageAttribute>();
18-
private readonly ConcurrentDictionary<string, Lazy<Task<object>>> eventStorageDict = new ConcurrentDictionary<string, Lazy<Task<object>>>();
17+
private readonly ConcurrentDictionary<Type, SnapshotStorageBaseAttribute> typeAttributes = new();
18+
private readonly ConcurrentDictionary<string, Lazy<Task<object>>> eventStorageDict = new();
1919
private readonly DbFactory dbFactory;
2020
private readonly IGrainFactory grainFactory;
2121
private readonly IServiceProvider serviceProvider;
@@ -31,23 +31,26 @@ public async ValueTask<ISubSnapshotStorage<TPrimaryKey>> Create<TPrimaryKey>(IAc
3131
{
3232
var attribute = this.typeAttributes.GetOrAdd(actor.GetType(), key =>
3333
{
34-
var attributes = key.GetCustomAttributes(typeof(SnapshotStorageAttribute), false);
35-
if (attributes.Length > 0)
34+
var attributes = key.GetCustomAttributes(false);
35+
var attribute = attributes.SingleOrDefault(att => typeof(SnapshotStorageBaseAttribute).IsAssignableFrom(att.GetType()));
36+
if (attribute != default)
3637
{
37-
return attributes.First() as SnapshotStorageAttribute;
38+
return attribute as SnapshotStorageBaseAttribute;
3839
}
3940
else
4041
{
41-
throw new MissingAttributeException($"{nameof(SnapshotStorageAttribute)}=>{key.Name}");
42+
throw new MissingAttributeException($"{nameof(SnapshotStorageBaseAttribute)}=>{key.Name}");
4243
}
4344
});
44-
var tableName = attribute.ShardingFunc(actor.ActorId.ToString());
45-
var storage = await this.eventStorageDict.GetOrAdd($"{attribute.OptionName}_{tableName}", key =>
45+
var actorId = actor.ActorId.ToString();
46+
var tableName = attribute.GetTableName(actorId);
47+
var optionName = attribute.GetOptionName(actorId);
48+
var storage = await this.eventStorageDict.GetOrAdd($"{optionName}_{tableName}", key =>
4649
new Lazy<Task<object>>(async () =>
4750
{
48-
using var db = this.dbFactory.GetEventDb(attribute.OptionName);
51+
using var db = this.dbFactory.GetEventDb(optionName);
4952
await db.CreateTableIfNotExists<SubSnapshotEntity<TPrimaryKey>>(this.grainFactory, key, tableName);
50-
return new SubSnapshotStorage<TPrimaryKey>(this.serviceProvider, this.dbFactory, attribute.OptionName, tableName);
53+
return new SubSnapshotStorage<TPrimaryKey>(this.serviceProvider, this.dbFactory, optionName, tableName);
5154
})).Value;
5255

5356
return storage as ISubSnapshotStorage<TPrimaryKey>;

0 commit comments

Comments
 (0)