Skip to content
Open
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
37 changes: 21 additions & 16 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,24 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Running Azurite
run: docker run -d -p 10000:10000 -p 10001:10001 mcr.microsoft.com/azure-storage/azurite
- name: Test
run: dotnet test --no-restore
- name: Create the package
run: dotnet pack --configuration Release
- name: Publish the package to NUGET
env: # Set the secret as an input
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
if: ${{ github.ref == 'refs/heads/main' }}
run: dotnet nuget push AutoNumber/bin/Release/*.nupkg --source https://api.nuget.org/v3/index.json --api-key $NUGET_API_KEY
- uses: actions/checkout@v2
- uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Running Azurite
run: docker run -d -p 10000:10000 -p 10001:10001 mcr.microsoft.com/azure-storage/azurite:latest
- name: Test
run: dotnet test --no-restore
- name: Create the package
run: dotnet pack --configuration Release
- name: Publish the package to NUGET
env: # Set the secret as an input
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
if: ${{ github.ref == 'refs/heads/main' }}
run: dotnet nuget push AutoNumber/bin/Release/*.nupkg --source https://api.nuget.org/v3/index.json --api-key $NUGET_API_KEY
80 changes: 41 additions & 39 deletions AutoNumber/AutoNumber.csproj
Original file line number Diff line number Diff line change
@@ -1,41 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<ProductVersion>8.0.30703</ProductVersion>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<AssemblyTitle>SnowMaker</AssemblyTitle>
<Product>AzureAutoNumber</Product>
<Description>High performance, distributed unique id generator for Azure environments.</Description>
<Version>1.4.0</Version>
<AssemblyVersion>1.5.0.0</AssemblyVersion>
<FileVersion>1.5.0.0</FileVersion>
<OutputPath>bin\$(Configuration)\</OutputPath>
<Authors>Ali Bahraminezhad</Authors>
<PackageLicenseExpression>MS-PL</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/0x414c49/AzureAutoNumber</PackageProjectUrl>
<PackageTags>Azure</PackageTags>
<Title>AutoNumber</Title>
<AssemblyName>AutoNumber</AssemblyName>
<RootNamespace>AutoNumber</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageReleaseNotes>* .NET 8
* All azure and other nuget dependencies upgraded</PackageReleaseNotes>
<PackageId>AzureAutoNumber</PackageId>
<PackageVersion>1.5.0</PackageVersion>
<SignAssembly>False</SignAssembly>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.13.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
</ItemGroup>
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0;net9.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<ProductVersion>8.0.30703</ProductVersion>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<AssemblyTitle>SnowMaker</AssemblyTitle>
<Product>AzureAutoNumber</Product>
<Description>High performance, distributed unique id generator for Azure environments.</Description>
<Version>1.5.2</Version>
<AssemblyVersion>1.5.2.0</AssemblyVersion>
<LangVersion>9.0</LangVersion>
<FileVersion>1.5.2.0</FileVersion>
<OutputPath>bin\$(Configuration)\</OutputPath>
<Authors>Ali Bahraminezhad</Authors>
<PackageLicenseExpression>MS-PL</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/0x414c49/AzureAutoNumber</PackageProjectUrl>
<PackageTags>Azure</PackageTags>
<Title>AutoNumber</Title>
<AssemblyName>AutoNumber</AssemblyName>
<RootNamespace>AutoNumber</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageReleaseNotes>* .NET 8
* All azure and other nuget dependencies upgraded
</PackageReleaseNotes>
<PackageId>AzureAutoNumber</PackageId>
<PackageVersion>1.5.0</PackageVersion>
<SignAssembly>False</SignAssembly>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.25.0"/>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.7"/>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.7"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.7"/>
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.7"/>
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.6.3"/>
</ItemGroup>
</Project>
32 changes: 12 additions & 20 deletions AutoNumber/BlobOptimisticDataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class BlobOptimisticDataStore : IOptimisticDataStore
private const string SeedValue = "1";
private readonly BlobContainerClient blobContainer;
private readonly ConcurrentDictionary<string, BlockBlobClient> blobReferences;
private readonly object blobReferencesLock = new object();
private readonly object blobReferencesLock = new();

public BlobOptimisticDataStore(BlobServiceClient blobServiceClient, string containerName)
{
Expand All @@ -36,22 +36,18 @@ public string GetData(string blockName)
{
var blobReference = GetBlobReference(blockName);

using (var stream = new MemoryStream())
{
blobReference.DownloadTo(stream);
return Encoding.UTF8.GetString(stream.ToArray());
}
using var stream = new MemoryStream();
blobReference.DownloadTo(stream);
return Encoding.UTF8.GetString(stream.ToArray());
}

public async Task<string> GetDataAsync(string blockName)
{
var blobReference = GetBlobReference(blockName);

using (var stream = new MemoryStream())
{
await blobReference.DownloadToAsync(stream).ConfigureAwait(false);
return Encoding.UTF8.GetString(stream.ToArray());
}
using var stream = new MemoryStream();
await blobReference.DownloadToAsync(stream).ConfigureAwait(false);
return Encoding.UTF8.GetString(stream.ToArray());
}

public async Task<bool> InitAsync()
Expand All @@ -73,7 +69,7 @@ public bool TryOptimisticWrite(string blockName, string data)
{
var blobRequestCondition = new BlobRequestConditions
{
IfMatch = (blobReference.GetProperties()).Value.ETag
IfMatch = blobReference.GetProperties().Value.ETag
};
UploadText(
blobReference,
Expand Down Expand Up @@ -155,10 +151,8 @@ private async Task UploadTextAsync(BlockBlobClient blob, string text, BlobReques
ContentType = "text/plain"
};

using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(text)))
{
await blob.UploadAsync(stream, header, null, accessCondition, null, null).ConfigureAwait(false);
}
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(text));
await blob.UploadAsync(stream, header, null, accessCondition).ConfigureAwait(false);
}

private void UploadText(BlockBlobClient blob, string text, BlobRequestConditions accessCondition)
Expand All @@ -168,10 +162,8 @@ private void UploadText(BlockBlobClient blob, string text, BlobRequestConditions
ContentType = "text/plain"
};

using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(text)))
{
blob.Upload(stream, header, null, accessCondition, null, null);
}
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(text));
blob.Upload(stream, header, null, accessCondition);
}
}
}
6 changes: 2 additions & 4 deletions AutoNumber/DebugOnlyFileDataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ public string GetData(string blockName)
{
var file = File.Create(blockPath);

using (var streamWriter = new StreamWriter(file))
{
streamWriter.Write(SeedValue);
}
using var streamWriter = new StreamWriter(file);
streamWriter.Write(SeedValue);

return SeedValue;
}
Expand Down
2 changes: 1 addition & 1 deletion AutoNumber/Options/AutoNumberOptionsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public AutoNumberOptionsBuilder(IConfiguration configuration)
configuration.GetSection(AutoNumber).Bind(Options);
}

public AutoNumberOptions Options { get; } = new AutoNumberOptions();
public AutoNumberOptions Options { get; } = new();

/// <summary>
/// Uses the default StorageAccount already defined in dependency injection
Expand Down
2 changes: 1 addition & 1 deletion AutoNumber/ScopeState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace AutoNumber
{
internal class ScopeState
{
public readonly object IdGenerationLock = new object();
public readonly object IdGenerationLock = new();
public long HighestIdAvailableInBatch;
public long LastId;
}
Expand Down
4 changes: 2 additions & 2 deletions AutoNumber/UniqueIdGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private void UpdateFromSyncStore(string scopeName, ScopeState state)
var firstIdInNextBatch = state.HighestIdAvailableInBatch + 1;

if (optimisticDataStore.TryOptimisticWrite(scopeName,
firstIdInNextBatch.ToString(CultureInfo.InvariantCulture)))
firstIdInNextBatch.ToString(CultureInfo.InvariantCulture)))
return;

writesAttempted++;
Expand All @@ -72,7 +72,7 @@ private void UpdateFromSyncStore(string scopeName, ScopeState state)

private readonly IOptimisticDataStore optimisticDataStore;
private readonly IDictionary<string, ScopeState> states = new Dictionary<string, ScopeState>();
private readonly object statesLock = new object();
private readonly object statesLock = new();
private int maxWriteAttempts = 25;

#endregion
Expand Down
81 changes: 39 additions & 42 deletions IntegrationTests/Azure.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,55 @@
using Azure.Storage.Blobs.Specialized;
using NUnit.Framework;

namespace IntegrationTests.cs
{
[TestFixture]
public class Azure : Scenarios<TestScope>
{
private readonly BlobServiceClient blobServiceClient = new BlobServiceClient("UseDevelopmentStorage=true");
namespace IntegrationTests.cs;

protected override TestScope BuildTestScope()
{
return new TestScope(new BlobServiceClient("UseDevelopmentStorage=true"));
}
[TestFixture]
public class Azure : Scenarios<TestScope>
{
private readonly BlobServiceClient blobServiceClient = new("UseDevelopmentStorage=true");

protected override IOptimisticDataStore BuildStore(TestScope scope)
{
var blobOptimisticDataStore = new BlobOptimisticDataStore(blobServiceClient, scope.ContainerName);
blobOptimisticDataStore.Init();
return blobOptimisticDataStore;
}
protected override TestScope BuildTestScope()
{
return new TestScope(new BlobServiceClient("UseDevelopmentStorage=true"));
}

public sealed class TestScope : ITestScope
protected override IOptimisticDataStore BuildStore(TestScope scope)
{
private readonly BlobServiceClient blobServiceClient;
var blobOptimisticDataStore = new BlobOptimisticDataStore(blobServiceClient, scope.ContainerName);
blobOptimisticDataStore.Init();
return blobOptimisticDataStore;
}
}

public sealed class TestScope : ITestScope
{
private readonly BlobServiceClient blobServiceClient;

public TestScope(BlobServiceClient blobServiceClient)
{
var ticks = DateTime.UtcNow.Ticks;
IdScopeName = string.Format("autonumbertest{0}", ticks);
ContainerName = string.Format("autonumbertest{0}", ticks);
public TestScope(BlobServiceClient blobServiceClient)
{
var ticks = DateTime.UtcNow.Ticks;
IdScopeName = string.Format("autonumbertest{0}", ticks);
ContainerName = string.Format("autonumbertest{0}", ticks);

this.blobServiceClient = blobServiceClient;
}
this.blobServiceClient = blobServiceClient;
}

public string ContainerName { get; }
public string ContainerName { get; }

public string IdScopeName { get; }
public string IdScopeName { get; }

public string ReadCurrentPersistedValue()
{
var blobContainer = blobServiceClient.GetBlobContainerClient(ContainerName);
var blob = blobContainer.GetBlockBlobClient(IdScopeName);
using (var stream = new MemoryStream())
{
blob.DownloadToAsync(stream).GetAwaiter().GetResult();
return Encoding.UTF8.GetString(stream.ToArray());
}
}
public string ReadCurrentPersistedValue()
{
var blobContainer = blobServiceClient.GetBlobContainerClient(ContainerName);
var blob = blobContainer.GetBlockBlobClient(IdScopeName);
using var stream = new MemoryStream();
blob.DownloadToAsync(stream).GetAwaiter().GetResult();
return Encoding.UTF8.GetString(stream.ToArray());
}

public void Dispose()
{
var blobContainer = blobServiceClient.GetBlobContainerClient(ContainerName);
blobContainer.DeleteAsync().GetAwaiter().GetResult();
}
public void Dispose()
{
var blobContainer = blobServiceClient.GetBlobContainerClient(ContainerName);
blobContainer.DeleteAsync().GetAwaiter().GetResult();
}
}
Loading
Loading