Skip to content

Commit

Permalink
VCST-122: Refactored Sequence Number Generator (#223)
Browse files Browse the repository at this point in the history
feat: Refactored Unique Number Generator
  • Loading branch information
OlegoO committed Feb 1, 2024
1 parent 4ad2990 commit ca25f89
Show file tree
Hide file tree
Showing 16 changed files with 848 additions and 28 deletions.
63 changes: 63 additions & 0 deletions src/VirtoCommerce.CoreModule.Core/Common/CounterOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Text.RegularExpressions;

namespace VirtoCommerce.CoreModule.Core.Common
{
/// <summary>
/// Represents counter options for ITenantUniqueNumberGenerator.
/// </summary>
public class CounterOptions
{
private static Regex NumberTemplateRegex = new Regex(@"(?<Template>[^@]+)@(?<ResetCounterType>None|Daily|Weekly|Monthly|Yearly)(:(?<StartCounterFrom>\d+))?(:(?<CounterIncrement>\d+))?", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase);

/// <summary>
/// Represents number temp[late
/// </summary>
public string NumberTemplate { get; set; }

/// <summary>
/// Represents type of counter reset. Can be one of this value: None, Daily, Weekly, Monthly, Yearly. Default value: Daily
/// </summary>
public ResetCounterType ResetCounterType { get; set; } = ResetCounterType.Daily;
/// <summary>
/// Represents start counter from value. Default value: 1
/// </summary>
public int StartCounterFrom { get; set; } = 1;
/// <summary>
/// Represents counter increment value. Default value: 1
/// </summary>
public int CounterIncrement { get; set; } = 1;

public override string ToString()
{
return $"{NumberTemplate}@{ResetCounterType}:{StartCounterFrom}:{CounterIncrement}";
}

/// <summary>
/// Parse counter options from string template: "numberTemplate" or "number_template@reset_counter_type:start_counter_from:counter_increment".
/// </summary>
/// <param name="template"></param>
/// <returns></returns>
public static CounterOptions Parse(string template)
{
ArgumentNullException.ThrowIfNull(template);

var match = NumberTemplateRegex.Match(template);

if (match.Success)
{
return new CounterOptions
{
NumberTemplate = match.Groups["Template"].Value,
ResetCounterType = Enum.Parse<ResetCounterType>(match.Groups["ResetCounterType"].Value),
StartCounterFrom = string.IsNullOrEmpty(match.Groups["StartCounterFrom"].Value) ? 1 : int.Parse(match.Groups["StartCounterFrom"].Value),
CounterIncrement = string.IsNullOrEmpty(match.Groups["CounterIncrement"].Value) ? 1 : int.Parse(match.Groups["CounterIncrement"].Value),
};
}
else
{
return new CounterOptions { NumberTemplate = template };
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace VirtoCommerce.CoreModule.Core.Common
{
/// <summary>
/// Represents interface for unique number generation for tenant.
/// </summary>
public interface ITenantUniqueNumberGenerator
{
/// <summary>
/// Generates unique number using given template with resetCounterType for tentantId.
/// </summary>
/// <param name="tenantId"></param>
/// <param name="counterOptions"></param>
/// <returns></returns>
string GenerateNumber(string tenantId, CounterOptions counterOptions);

/// <summary>
/// Generates unique number using given template for tentantId.
/// </summary>
/// <param name="tenantId"></param>
/// <param name="numberTemplate"></param>
/// <returns></returns>
string GenerateNumber(string tenantId, string numberTemplate);
}
}
11 changes: 11 additions & 0 deletions src/VirtoCommerce.CoreModule.Core/Common/ResetCounterType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace VirtoCommerce.CoreModule.Core.Common
{
public enum ResetCounterType
{
None = 0,
Daily = 1,
Weekly = 2,
Monthly = 3,
Yearly = 5,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace VirtoCommerce.CoreModule.Core.Common
{
public class SequenceNumberGeneratorOptions
{
/// <summary>
/// Defines the number of retries to generate unique number if either DbUpdateConcurrencyException or InvalidOperationException is occured.
/// </summary>
public int RetryCount { get; set; } = 15;
/// <summary>
/// Defines the delay between retries in seconds.
/// </summary>
public int RetryDelay { get; set; } = 5;
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace VirtoCommerce.CoreModule.Data.PostgreSql.Migrations
{
/// <inheritdoc />
public partial class UpdateRowVersion : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "RowVersion",
table: "Sequence",
newName: "xmin");

migrationBuilder.AlterColumn<uint>(
name: "xmin",
table: "Sequence",
type: "xid",
rowVersion: true,
nullable: true,
oldClrType: typeof(byte[]),
oldType: "bytea",
oldRowVersion: true,
oldNullable: true);
}

/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "xmin",
table: "Sequence",
newName: "RowVersion");

migrationBuilder.AlterColumn<byte[]>(
name: "RowVersion",
table: "Sequence",
type: "bytea",
rowVersion: true,
nullable: true,
oldClrType: typeof(uint),
oldType: "xid",
oldRowVersion: true,
oldNullable: true);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.0")
.HasAnnotation("ProductVersion", "8.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);

NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
Expand Down Expand Up @@ -92,10 +92,11 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property<DateTime?>("ModifiedDate")
.HasColumnType("timestamp with time zone");

b.Property<byte[]>("RowVersion")
b.Property<long?>("RowVersion")
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("bytea");
.HasColumnType("xid")
.HasColumnName("xmin");

b.Property<int>("Value")
.HasColumnType("integer");
Expand Down
29 changes: 12 additions & 17 deletions src/VirtoCommerce.CoreModule.Data.PostgreSql/Readme.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
# Generate Migrations

## Package manager
Add-Migration Initial -Context VirtoCommerce.CoreModule.Data.Repositories.CoreDbContext -Verbose -OutputDir Migrations -Project VirtoCommerce.CoreModule.Data.PostgreSql -StartupProject VirtoCommerce.CoreModule.Data.PostgreSql -Debug



### Entity Framework Core Commands
```
dotnet tool install --global dotnet-ef --version 6.*
## Install CLI tools for Entity Framework Core
```cmd
dotnet tool install --global dotnet-ef --version 8.0.0
```

**Generate Migrations**
or update

```cmd
dotnet tool update --global dotnet-ef --version 8.0.0
```
dotnet ef migrations add Initial -- "{connection string}"
dotnet ef migrations add Update1 -- "{connection string}"
dotnet ef migrations add Update2 -- "{connection string}"
```

etc..

**Apply Migrations**
## Add Migration
Select Data.<Provider> folder and run following command for each provider:

`dotnet ef database update -- "{connection string}"`
```cmd
dotnet ef migrations add <migration-name>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using VirtoCommerce.CoreModule.Data.Model;

namespace VirtoCommerce.CoreModule.Data.PostgreSql
{
public class SequenceEntityConfiguration : IEntityTypeConfiguration<SequenceEntity>
{
public void Configure(EntityTypeBuilder<SequenceEntity> builder)
{
var converter = new ValueConverter<byte[], long>(
v => BitConverter.ToInt64(v, 0),
v => BitConverter.GetBytes(v));

builder.Property(c => c.RowVersion)
.HasColumnType("xid")
.HasColumnName("xmin")
.HasConversion(converter)
.ValueGeneratedOnAddOrUpdate()
.IsConcurrencyToken();
}
}
}
Loading

0 comments on commit ca25f89

Please sign in to comment.