Skip to content

Commit

Permalink
Merge pull request #36 from markjbrown/mjb-updates
Browse files Browse the repository at this point in the history
Mjb updates
  • Loading branch information
markjbrown authored Oct 10, 2024
2 parents 8311674 + 82d1a0b commit 97a4695
Show file tree
Hide file tree
Showing 11 changed files with 53 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/devcontainers/dotnet:6.0-bullseye
FROM mcr.microsoft.com/dotnet/sdk:8.0

# Install Azure Functions Core Tools
RUN curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
Expand Down
1 change: 1 addition & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"extensions": [
"ms-dotnettools.vscode-dotnet-runtime",
"ms-dotnettools.csharp",
"ms-azuretools.vscode-docker",
"ms-azuretools.vscode-bicep"
]
}
Expand Down
12 changes: 6 additions & 6 deletions distributed-counter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Typically, you can avoid concurrency issues by implementing optimistic concurren

## Solution

In a distributed counter solution, a group of distributed counter items are used to keep track of the number. By having the solution distribute the counter across multiple items, update operations can be performed on a random item without causing contention. Even more, the solution can calculate the total of all counters at any time using an aggregation of the values from each individual counter.
In a distributed counter solution, a group of distributed counters, implemented as documents, are used to keep track of the number. By having the solution distribute the counter across multiple documents, update operations can be performed on a random document without causing contention. Calculating the current counter value can be done at any time by performing an aggregation of the values across all the documents in a query.

## Sample implementation

Expand Down Expand Up @@ -89,7 +89,7 @@ You need to configure **two** application configuration files to run this app.

While on the Keys blade, make note of the `URI` and `PRIMARY KEY`. You will need these for the sections below.

1. Open the Visualizer project and add a new **appsettings.development.json** file with the following contents:
1. Open the **Visualizer** project and add a new **appsettings.development.json** file with the following contents:

```json
{
Expand All @@ -115,7 +115,7 @@ While on the Keys blade, make note of the `URI` and `PRIMARY KEY`. You will need

Next move to the other project.

1. Open the ConsumerApp project and add a new **appsettings.development.json** file with the following contents:
1. Open the **ConsumerApp** project and add a new **appsettings.development.json** file with the following contents:

```json
{
Expand Down Expand Up @@ -199,10 +199,10 @@ Next move to the other project.
## Summary
In conclusion, the Distributed Counter design pattern offers a powerful solution for managing count-related data in NoSQL databases. By leveraging distributed systems' capabilities, this pattern enables the seamless tracking of numeric values across various nodes, ensuring accuracy and scalability. Through careful design and implementation, applications can efficiently handle scenarios involving likes, votes, or any form of quantifiable interactions.
In conclusion, the Distributed Counter design pattern offers a powerful solution for managing count-related data in NoSQL databases. By leveraging multiple documents and randomizing access to reduce concurrency issues, this pattern enables the tracking of numeric values at any scale with accuracy. Through careful design and implementation, applications can efficiently handle scenarios involving likes, votes, product inventory or any form of quantifiable interactions where concurrency is an issue.
The beauty of the Distributed Counter lies in its ability to maintain consistency in a distributed environment, achieving high availability and fault tolerance. By leveraging atomic operations and optimized data structures, it minimizes contention while delivering rapid and accurate count updates.
The beauty of the Distributed Counter lies in its ability to maintain consistency, achieving high availability and fault tolerance. By leveraging atomic operations and optimized data structures, it minimizes contention while delivering rapid and accurate count updates.
From social media interactions to monitoring system metrics, the Distributed Counter pattern empowers applications to handle dynamic, high-velocity scenarios. By incorporating this pattern, developers can harness the full potential of NoSQL databases, ensuring reliable count management that scales alongside user engagement and system growth.
From social media interactions, ecommerce or monitoring high-volume system metrics, the Distributed Counter pattern empowers applications to handle dynamic, high-velocity scenarios. By incorporating this pattern, developers can harness the full potential of NoSQL databases, ensuring reliable count management that scales alongside user engagement and system growth.
As technology continues to evolve, and as user interactions become increasingly diverse and complex, the Distributed Counter design pattern remains an essential tool in a developer's toolkit, providing a solid foundation for effective count management in the dynamic world of modern distributed applications.
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<None Remove="appsettings - Copy.json" />
<None Remove="appsettings.development.json" />

<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@
</ItemGroup>

<ItemGroup>
<Content Remove="C:\Users\sandnair\.nuget\packages\microsoft.azure.cosmos\3.31.2\contentFiles\any\netstandard2.0\ThirdPartyNotice.txt" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.41.0" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.4*" />
</ItemGroup>

</Project>
4 changes: 2 additions & 2 deletions distributed-counter/source/Visualizer/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<DistributedCounterManagementService>(InitializeDistributedCounterManagementServiceAsync(Configuration).GetAwaiter().GetResult());
services.AddSingleton<DistributedCounterManagementService>(InitializeDistributedCounterManagementServiceAsync(Configuration));
}


private static async Task<DistributedCounterManagementService> InitializeDistributedCounterManagementServiceAsync(IConfiguration configuration)
private static DistributedCounterManagementService InitializeDistributedCounterManagementServiceAsync(IConfiguration configuration)
{

string endpoint = configuration["CosmosUri"];
Expand Down
2 changes: 1 addition & 1 deletion event-sourcing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ While event sourcing can be implemented with various types of databases, this pa

1. Flexible schema: NoSQL databases generally allow for schema flexibility. Easy support for unstructured event data formats that are often in JSON formats align perfectly with the needs of event sourcing architectures.

1. Scalability: NoSQL databases are typically designed for high scale. Data volumes in event sourcing patterns can range from the thousands to millions of messages per second. An underlying database needs to scale and do so seemlessly. Azure Cosmos DB's scale-out architecture is well-suited here with highly elastic throughput and storage.
1. Scalability: NoSQL databases are typically designed for high scale. Data volumes in event sourcing patterns can range from the thousands to millions of messages per second. An underlying database needs to scale and do so seamlessly. Azure Cosmos DB's scale-out architecture is well-suited here with highly elastic throughput and storage.

This sample demonstrates:

Expand Down
2 changes: 1 addition & 1 deletion event-sourcing/source/CartEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class CartEvent
public int UserId { get; set; }
public string EventType { get; set; } = "";
public string? Product { get; set; }
public int? QuantityChange { get; set; }
public int? QuantityChange { get; set; } = 0;
public List<CartItem>? ProductsInCart { get; set; }
public string EventTimestamp { get; set; } = DateTime.UtcNow.ToString();
}
Expand Down
20 changes: 10 additions & 10 deletions event-sourcing/source/EventSourceFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ namespace EventSourcing
{
public class EventSourceFunction
{
private readonly ILogger _logger;
private readonly ILogger<EventSourceFunction> _logger;

public EventSourceFunction(ILoggerFactory loggerFactory)
public EventSourceFunction(ILogger<EventSourceFunction> logger)
{
_logger = loggerFactory.CreateLogger<EventSourceFunction>();
_logger = logger;
}

[Function("EventSourcing")]
Expand All @@ -25,22 +25,22 @@ public EventSourceFunction(ILoggerFactory loggerFactory)
Connection = "CosmosDBConnection",
CreateIfNotExists = true,
PartitionKey = "/CartId")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
IAsyncCollector<CartEvent> cartEventOut,
ILogger log)
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
FunctionContext context)
{
log.LogInformation("C# HTTP trigger function processed a request.");
_logger.LogInformation("C# HTTP trigger function processed a request.");

string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
string responseMessage;

if (requestBody != null)
{
CartEvent cartEvent = JsonConvert.DeserializeObject<CartEvent>(requestBody) ?? throw new ArgumentException("Request body is empty");
await cartEventOut.AddAsync(cartEvent);
_logger.LogInformation(JsonConvert.SerializeObject(cartEvent, Formatting.Indented));

responseMessage = $"HTTP function successful for event {cartEvent.EventType} for cart {cartEvent.CartId}.";
//responseMessage = $"HTTP function successful for event {cartEvent.EventType} for cart {cartEvent.CartId}.";
return new OkObjectResult(cartEvent);
}
else
{
Expand Down
10 changes: 8 additions & 2 deletions event-sourcing/source/EventSourcing.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<None Update="local.settings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.21.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.CosmosDB" Version="4.6.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.2.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.2.1" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.4" />
<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="1.2.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.CosmosDB" Version="4.7.0" />
Expand Down
40 changes: 21 additions & 19 deletions event-sourcing/source/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,6 @@
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;

var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureServices(services =>
{
services.AddApplicationInsightsTelemetryWorkerService();
services.ConfigureFunctionsApplicationInsights();
})
.Build();

host.Run();

namespace EventSourcing
{
Expand All @@ -23,10 +13,11 @@ internal class Program

public static async Task<string> CreateCartEvent(HttpClient client, CartEvent cartEvent)
{
var url = $"{urlBase}/api/EventSourceFunction";
var url = $"{urlBase}/api/EventSourcing";

string jsonBody = JsonConvert.SerializeObject(cartEvent);
var body = new StringContent(jsonBody, System.Text.Encoding.UTF8, "application/json");
Console.WriteLine(jsonBody);

var response = await client.PostAsync(url, body);
string result = await response.Content.ReadAsStringAsync();
Expand Down Expand Up @@ -108,7 +99,7 @@ public static List<CartEvent> GenerateCartEvents()
cartEvent.SessionId = sessionId;
cartEvent.UserId = userId;
cartEvent.EventType = action;
cartEvent.ProductsInCart = null;
cartEvent.ProductsInCart = new List<CartItem>();
cartEvents.Add(cartEvent);
}
}
Expand All @@ -118,15 +109,25 @@ public static List<CartEvent> GenerateCartEvents()
static async Task Main(string[] args)
{

HttpClient httpClient = new HttpClient();
if (args.Length > 0 && args[0] != "console")
{
var host = new HostBuilder()
.ConfigureFunctionsWebApplication()
.ConfigureServices(services =>
{
services.AddApplicationInsightsTelemetryWorkerService();
services.ConfigureFunctionsApplicationInsights();
})
.Build();

await host.RunAsync();
}

else
{
HttpClient httpClient = new HttpClient();

//var services = new ServiceCollection();
//services.AddHttpClient(); // Add the HttpClientFactory service
//services.AddLogging(); // Add the logging service
//var serviceProvider = services.BuildServiceProvider();

//// Resolve the HttpClient from the service provider
//var httpClient = serviceProvider.GetRequiredService<HttpClient>();
httpClient.Timeout = TimeSpan.FromMinutes(10);

Console.WriteLine("This code will demonstrate the Event Sourcing pattern by saving shopping cart events to Azure Cosmos DB for NoSQL account.");
Expand All @@ -151,6 +152,7 @@ static async Task Main(string[] args)

System.Console.WriteLine($"Function completed generation of shopping cart events");
Console.WriteLine($"Check CartEventContainer for new shopping cart events");
}
}
}
}

0 comments on commit 97a4695

Please sign in to comment.