From 8c7e301dad3b232f7eb57a352896e8987225c1f6 Mon Sep 17 00:00:00 2001 From: James Eastham Date: Fri, 10 Nov 2023 22:31:28 +0000 Subject: [PATCH] Remove net core 3.1, Lambda out of support --- README.md | 80 +++------- .../DeleteProduct/DeleteProduct.csproj | 18 --- src/NET31-OTel/DeleteProduct/Function.cs | 80 ---------- .../GenerateLoadTestResults/DateUtils.cs | 13 -- .../GenerateLoadTestResults/Function.cs | 146 ----------------- .../GenerateLoadTestResults.csproj | 17 -- .../GenerateLoadTestResults/QueryResult.cs | 25 --- src/NET31-OTel/GetProduct/Function.cs | 78 --------- src/NET31-OTel/GetProduct/GetProduct.csproj | 18 --- src/NET31-OTel/GetProducts/Function.cs | 58 ------- src/NET31-OTel/GetProducts/GetProducts.csproj | 18 --- src/NET31-OTel/PutProduct/Function.cs | 80 ---------- src/NET31-OTel/PutProduct/PutProduct.csproj | 18 --- .../Shared/DataAccess/DynamoDbProducts.cs | 62 -------- .../Shared/DataAccess/ProductMapper.cs | 33 ---- .../Shared/DataAccess/ProductsDAO.cs | 16 -- src/NET31-OTel/Shared/Models/Product.cs | 38 ----- .../Shared/Models/ProductWrapper.cs | 16 -- src/NET31-OTel/Shared/Shared.csproj | 24 --- src/NET31-OTel/Shared/Tracing.cs | 27 ---- src/NET31-OTel/Shared/config.yaml | 25 --- src/NET31-OTel/template.yaml | 149 ------------------ src/NET31/DeleteProduct/DeleteProduct.csproj | 19 --- src/NET31/DeleteProduct/Function.cs | 71 --------- .../GenerateLoadTestResults/DateUtils.cs | 13 -- src/NET31/GenerateLoadTestResults/Function.cs | 146 ----------------- .../GenerateLoadTestResults.csproj | 17 -- .../GenerateLoadTestResults/QueryResult.cs | 25 --- src/NET31/GetProduct/Function.cs | 70 -------- src/NET31/GetProduct/GetProduct.csproj | 19 --- src/NET31/GetProducts/Function.cs | 50 ------ src/NET31/GetProducts/GetProducts.csproj | 19 --- src/NET31/PutProduct/Function.cs | 72 --------- src/NET31/PutProduct/PutProduct.csproj | 19 --- .../Shared/DataAccess/DynamoDbProducts.cs | 62 -------- src/NET31/Shared/DataAccess/ProductMapper.cs | 33 ---- src/NET31/Shared/DataAccess/ProductsDAO.cs | 16 -- src/NET31/Shared/Models/Product.cs | 38 ----- src/NET31/Shared/Models/ProductWrapper.cs | 16 -- src/NET31/Shared/Shared.csproj | 11 -- src/NET31/template.yaml | 145 ----------------- 41 files changed, 23 insertions(+), 1877 deletions(-) delete mode 100755 src/NET31-OTel/DeleteProduct/DeleteProduct.csproj delete mode 100755 src/NET31-OTel/DeleteProduct/Function.cs delete mode 100644 src/NET31-OTel/GenerateLoadTestResults/DateUtils.cs delete mode 100644 src/NET31-OTel/GenerateLoadTestResults/Function.cs delete mode 100644 src/NET31-OTel/GenerateLoadTestResults/GenerateLoadTestResults.csproj delete mode 100644 src/NET31-OTel/GenerateLoadTestResults/QueryResult.cs delete mode 100755 src/NET31-OTel/GetProduct/Function.cs delete mode 100755 src/NET31-OTel/GetProduct/GetProduct.csproj delete mode 100755 src/NET31-OTel/GetProducts/Function.cs delete mode 100755 src/NET31-OTel/GetProducts/GetProducts.csproj delete mode 100755 src/NET31-OTel/PutProduct/Function.cs delete mode 100755 src/NET31-OTel/PutProduct/PutProduct.csproj delete mode 100755 src/NET31-OTel/Shared/DataAccess/DynamoDbProducts.cs delete mode 100755 src/NET31-OTel/Shared/DataAccess/ProductMapper.cs delete mode 100755 src/NET31-OTel/Shared/DataAccess/ProductsDAO.cs delete mode 100755 src/NET31-OTel/Shared/Models/Product.cs delete mode 100755 src/NET31-OTel/Shared/Models/ProductWrapper.cs delete mode 100755 src/NET31-OTel/Shared/Shared.csproj delete mode 100755 src/NET31-OTel/Shared/Tracing.cs delete mode 100755 src/NET31-OTel/Shared/config.yaml delete mode 100755 src/NET31-OTel/template.yaml delete mode 100755 src/NET31/DeleteProduct/DeleteProduct.csproj delete mode 100755 src/NET31/DeleteProduct/Function.cs delete mode 100644 src/NET31/GenerateLoadTestResults/DateUtils.cs delete mode 100644 src/NET31/GenerateLoadTestResults/Function.cs delete mode 100644 src/NET31/GenerateLoadTestResults/GenerateLoadTestResults.csproj delete mode 100644 src/NET31/GenerateLoadTestResults/QueryResult.cs delete mode 100755 src/NET31/GetProduct/Function.cs delete mode 100755 src/NET31/GetProduct/GetProduct.csproj delete mode 100755 src/NET31/GetProducts/Function.cs delete mode 100755 src/NET31/GetProducts/GetProducts.csproj delete mode 100755 src/NET31/PutProduct/Function.cs delete mode 100755 src/NET31/PutProduct/PutProduct.csproj delete mode 100755 src/NET31/Shared/DataAccess/DynamoDbProducts.cs delete mode 100755 src/NET31/Shared/DataAccess/ProductMapper.cs delete mode 100755 src/NET31/Shared/DataAccess/ProductsDAO.cs delete mode 100755 src/NET31/Shared/Models/Product.cs delete mode 100755 src/NET31/Shared/Models/ProductWrapper.cs delete mode 100755 src/NET31/Shared/Shared.csproj delete mode 100755 src/NET31/template.yaml diff --git a/README.md b/README.md index 5dd44f1..950bb3f 100755 --- a/README.md +++ b/README.md @@ -12,8 +12,6 @@ The application consists of an [Amazon API Gateway](https://aws.amazon.com/api-g It includes the below implementations as well as benchmarking results for both x86 and ARM64: -- .NET Core 3.1 -- .NET Core 3.1 with Open Telemetry tracing - .NET 6 Lambda - .NET 6 Top Level statements - .NET 6 Minimal API @@ -39,7 +37,7 @@ There are four implementations included in the repository, covering a variety of There is a separate project for each of the four Lambda functions, as well as a shared library that contains the data access implementations. It uses the hexagonal architecture pattern to decouple the entry points, from the main domain and storage logic. -### .NET 6 +## .NET 6 This implementation is the simplest route to upgrade a .NET Core 3.1 function to use .NET 6 as it only requires upgrading the function runtime, project target framework and any dependencies as per the final section of [this link](https://aws.amazon.com/blogs/compute/introducing-the-net-6-runtime-for-aws-lambda/). @@ -65,6 +63,8 @@ The code is compiled natively for either Linux-x86_64 or Linux-ARM64 and then de Details for compiling .NET 6 native AOT can be found [here](https://github.com/dotnet/runtimelab/blob/feature/NativeAOT/docs/using-nativeaot/compiling.md) +## .NET 7 + ### .NET 7 Custom Runtime The code is compiled on a custom runtime and deployed to the provided.al2 Lambda runtime because Lambda doesn't have a .NET 7 runtime. The code is compiled as ReadyToRun and Self-Contained because there is not .NET runtime on provided.al2 to depend on. This type of deployment is expected to be slower than a fully supported Lambda runtime like .NET 6. This sample should be able to be tested with `sam build` and then `sam deploy --guided`. @@ -81,6 +81,24 @@ There is a single project named ApiBootstrap that contains all the start-up code Details for compiling .NET 7 native AOT can be found [here](https://github.com/dotnet/runtimelab/blob/feature/NativeAOT/docs/using-nativeaot/compiling.md) +## .NET 8 + +### .NET 8 Custom Runtime + +The code is compiled on a custom runtime and deployed to the provided.al2 Lambda runtime because Lambda doesn't have a .NET 8 runtime. The code is compiled as ReadyToRun and Self-Contained because there is not .NET runtime on provided.al2 to depend on. This type of deployment is expected to be slower than a fully supported Lambda runtime like .NET 6. This sample should be able to be tested with `sam build` and then `sam deploy --guided`. + +### .NET 8 native AOT + +The code is compiled natively for Linux-x86_64 then deployed manually to Lambda as a zip file. + +Details for compiling .NET 7 native AOT can be found [here](https://github.com/dotnet/runtimelab/blob/feature/NativeAOT/docs/using-nativeaot/compiling.md) + +### .NET 8 minimal API with native AOT + +There is a single project named ApiBootstrap that contains all the start-up code and API endpoint mapping. The code is compiled natively for Linux-x86_64 then deployed manually to Lambda as a zip file. Microsoft have announced limited support for ASP.NET and native AOT in .NET 8, using the `WebApplication.CreateSlimBuilder(args);` method. + +Details for compiling .NET 8 native AOT can be found [here](https://github.com/dotnet/runtimelab/blob/feature/NativeAOT/docs/using-nativeaot/compiling.md) + ## Deployment To deploy the architecture into your AWS account, navigate into the respective folder under the src folder and run 'sam deploy --guided'. This will launch a deployment wizard, complete the required values to initiate the deployment. For example, for .NET 6: @@ -111,6 +129,8 @@ All latencies listed below are in milliseconds. [AWS Lambda Power Tuning](https://github.com/alexcasalboni/aws-lambda-power-tuning) is used to optimize the cost/performance. 1024MB of function memory provided the optimal balance between cost and performance. +For the .NET 8 Native AOT compiled example the optimal memory allocation was 3008mb. + ![](./imgs/power-tuning.PNG) ### Results @@ -123,60 +143,6 @@ filter @type="REPORT" | stats count(*) as count, pct(duration, 50) as p50, pct(duration, 90) as p90, pct(duration, 99) as p99, max(duration) as max by coldstart ``` -### .NET Core 3.1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cold Start (ms)Warm Start (ms)
p50p90p99maxp50p90p99max
ARM641122.701170.831225.921326.325.558.7419.85256.55
X861004.801135.811422.781786.786.1110.8229.40247.32
X86 with Open Telemetry1615.311704.931931.822067.977.0412.0835.571059.78
- ### .NET 6 diff --git a/src/NET31-OTel/DeleteProduct/DeleteProduct.csproj b/src/NET31-OTel/DeleteProduct/DeleteProduct.csproj deleted file mode 100755 index 1e2954b..0000000 --- a/src/NET31-OTel/DeleteProduct/DeleteProduct.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - netcoreapp3.1 - true - - - - - - - - - - - - - diff --git a/src/NET31-OTel/DeleteProduct/Function.cs b/src/NET31-OTel/DeleteProduct/Function.cs deleted file mode 100755 index a2541f7..0000000 --- a/src/NET31-OTel/DeleteProduct/Function.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; - -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; -using OpenTelemetry.Contrib.Instrumentation.AWSLambda.Implementation; -using Shared; -using Shared.DataAccess; - -[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] - -namespace DeleteProduct -{ - public class Function - { - private readonly ProductsDAO dataAccess; - public Function() - { - Tracing.Init(); - - this.dataAccess = new DynamoDbProducts(); - } - - public Task TracingFunctionHandler(APIGatewayProxyRequest request, ILambdaContext context) - { - return AWSLambdaWrapper.Trace(Tracing.TraceProvider, FunctionHandler, request, context); - } - - public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, - ILambdaContext context) - { - if (!apigProxyEvent.HttpMethod.Equals(HttpMethod.Delete.Method)) - { - return new APIGatewayProxyResponse - { - Body = "Only DELETE allowed", - StatusCode = (int)HttpStatusCode.MethodNotAllowed, - }; - } - - try - { - var id = apigProxyEvent.PathParameters["id"]; - - var product = await dataAccess.GetProduct(id); - - if (product == null) - { - return new APIGatewayProxyResponse - { - Body = "Not Found", - StatusCode = (int)HttpStatusCode.NotFound, - }; - } - - await dataAccess.DeleteProduct(product.Id); - - return new APIGatewayProxyResponse - { - StatusCode = (int)HttpStatusCode.OK, - Body = $"Product with id {id} deleted" - }; - } - catch (Exception e) - { - context.Logger.LogLine($"Error deleting product {e.Message} {e.StackTrace}"); - - return new APIGatewayProxyResponse - { - Body = "Not Found", - StatusCode = (int)HttpStatusCode.InternalServerError, - }; - } - } - } -} diff --git a/src/NET31-OTel/GenerateLoadTestResults/DateUtils.cs b/src/NET31-OTel/GenerateLoadTestResults/DateUtils.cs deleted file mode 100644 index c37c43d..0000000 --- a/src/NET31-OTel/GenerateLoadTestResults/DateUtils.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace GenerateLoadTestResults; - -public static class DateUtils -{ - public static long AsUnixTimestamp(this DateTime date) - { - DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - TimeSpan diff = date.ToUniversalTime() - origin; - return (long)Math.Floor(diff.TotalSeconds); - } -} \ No newline at end of file diff --git a/src/NET31-OTel/GenerateLoadTestResults/Function.cs b/src/NET31-OTel/GenerateLoadTestResults/Function.cs deleted file mode 100644 index 21a57a8..0000000 --- a/src/NET31-OTel/GenerateLoadTestResults/Function.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; -using Amazon.CloudWatchLogs; -using Amazon.CloudWatchLogs.Model; -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; - -[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] - -namespace GenerateLoadTestResults -{ - public class Function - { - private AmazonCloudWatchLogsClient _cloudWatchLogsClient; - - public Function() - { - this._cloudWatchLogsClient = new AmazonCloudWatchLogsClient(); - } - - public async Task FunctionHandler( - APIGatewayHttpApiV2ProxyRequest apigProxyEvent, - ILambdaContext context) - { - if (!apigProxyEvent.RequestContext.Http.Method.Equals(HttpMethod.Get.Method)) - { - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Only GET allowed", - StatusCode = (int) HttpStatusCode.MethodNotAllowed, - }; - } - - try - { - var resultRows = 0; - var queryCount = 0; - - List> finalResults = new List>(); - - while (resultRows < 2 || queryCount >= 3) - { - finalResults = await runQuery(context); - - resultRows = finalResults.Count; - queryCount++; - } - - var wrapper = new QueryResultWrapper() - { - LoadTestType = - $"{Environment.GetEnvironmentVariable("LOAD_TEST_TYPE")} ({Environment.GetEnvironmentVariable("LAMBDA_ARCHITECTURE")})", - WarmStart = new QueryResult() - { - Count = finalResults[0][1].Value, - P50 = finalResults[0][2].Value, - P90 = finalResults[0][3].Value, - P99 = finalResults[0][4].Value, - Max = finalResults[0][5].Value, - }, - ColdStart = new QueryResult() - { - Count = finalResults[1][1].Value, - P50 = finalResults[1][2].Value, - P90 = finalResults[1][3].Value, - P99 = finalResults[1][4].Value, - Max = finalResults[1][5].Value, - } - }; - - return new APIGatewayHttpApiV2ProxyResponse - { - StatusCode = (int) HttpStatusCode.OK, - Body = wrapper.AsMarkdownTableRow(), - Headers = new Dictionary {{"Content-Type", "text/html"}} - }; - } - catch (Exception e) - { - context.Logger.LogLine($"Error retrieving results {e.Message} {e.StackTrace}"); - - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Not Found", - StatusCode = (int) HttpStatusCode.InternalServerError, - }; - } - } - - private async Task>> runQuery(ILambdaContext context) - { - var logGroupNamePrefix = - $"{Environment.GetEnvironmentVariable("LOG_GROUP_PREFIX")}{Environment.GetEnvironmentVariable("LAMBDA_ARCHITECTURE")}" - .Replace("_", "-"); - - context.Logger.LogLine($"Retrieving log groups with prefix {logGroupNamePrefix}"); - - var logGroupList = await _cloudWatchLogsClient.DescribeLogGroupsAsync(new DescribeLogGroupsRequest() - { - LogGroupNamePrefix = logGroupNamePrefix, - }); - - context.Logger.LogLine($"Found {logGroupList.LogGroups.Count} log group(s)"); - - var queryRes = await _cloudWatchLogsClient.StartQueryAsync(new StartQueryRequest() - { - LogGroupNames = logGroupList.LogGroups.Select(p => p.LogGroupName).ToList(), - QueryString = - "filter @type=\"REPORT\" | fields greatest(@initDuration, 0) + @duration as duration, ispresent(@initDuration) as coldstart | stats count(*) as count, pct(duration, 50) as p50, pct(duration, 90) as p90, pct(duration, 99) as p99, max(duration) as max by coldstart", - StartTime = DateTime.Now.AddMinutes(-20).AsUnixTimestamp(), - EndTime = DateTime.Now.AsUnixTimestamp(), - }); - - context.Logger.LogLine($"Running query, query id is {queryRes.QueryId}"); - - QueryStatus currentQueryStatus = QueryStatus.Running; - List> finalResults = new List>(); - - while (currentQueryStatus == QueryStatus.Running || currentQueryStatus == QueryStatus.Scheduled) - { - context.Logger.LogLine("Retrieving query results"); - - var queryResults = await _cloudWatchLogsClient.GetQueryResultsAsync(new GetQueryResultsRequest() - { - QueryId = queryRes.QueryId - }); - - context.Logger.LogLine($"Query result status is {queryResults.Status}"); - - currentQueryStatus = queryResults.Status; - finalResults = queryResults.Results; - - await Task.Delay(TimeSpan.FromSeconds(5)); - } - - context.Logger.LogLine($"Final results: {finalResults.Count} row(s)"); - - return finalResults; - } - } -} \ No newline at end of file diff --git a/src/NET31-OTel/GenerateLoadTestResults/GenerateLoadTestResults.csproj b/src/NET31-OTel/GenerateLoadTestResults/GenerateLoadTestResults.csproj deleted file mode 100644 index 4a8d708..0000000 --- a/src/NET31-OTel/GenerateLoadTestResults/GenerateLoadTestResults.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - net6.0 - true - true - - - - - - - - - - - diff --git a/src/NET31-OTel/GenerateLoadTestResults/QueryResult.cs b/src/NET31-OTel/GenerateLoadTestResults/QueryResult.cs deleted file mode 100644 index 69b5a31..0000000 --- a/src/NET31-OTel/GenerateLoadTestResults/QueryResult.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace GenerateLoadTestResults; - -public record QueryResultWrapper -{ - public string LoadTestType { get; set; } - - public QueryResult ColdStart { get; set; } - - public QueryResult WarmStart { get; set; } - - public string AsMarkdownTableRow() => $"
Cold Start (ms)Warm Start (ms)
p50p90p99maxp50p90p99max
{LoadTestType}{ColdStart.P50}{ColdStart.P90}{ColdStart.P99}{ColdStart.Max}{WarmStart.P50}{WarmStart.P90}{WarmStart.P99}{WarmStart.Max}
"; -} - -public record QueryResult -{ - public string Count { get; set; } - - public string P50 { get; set; } - - public string P90 { get; set; } - - public string P99 { get; set; } - - public string Max { get; set; } -} \ No newline at end of file diff --git a/src/NET31-OTel/GetProduct/Function.cs b/src/NET31-OTel/GetProduct/Function.cs deleted file mode 100755 index e51ecd4..0000000 --- a/src/NET31-OTel/GetProduct/Function.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; - -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; -using OpenTelemetry.Contrib.Instrumentation.AWSLambda.Implementation; -using Shared; -using Shared.DataAccess; - -[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] - -namespace GetProduct -{ - public class Function - { - private readonly ProductsDAO dataAccess; - public Function() - { - Tracing.Init(); - this.dataAccess = new DynamoDbProducts(); - } - - public Task TracingFunctionHandler(APIGatewayProxyRequest request, ILambdaContext context) - { - return AWSLambdaWrapper.Trace(Tracing.TraceProvider, FunctionHandler, request, context); - } - - public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, - ILambdaContext context) - { - if (!apigProxyEvent.HttpMethod.Equals(HttpMethod.Get.Method)) - { - return new APIGatewayProxyResponse - { - Body = "Only GET allowed", - StatusCode = (int)HttpStatusCode.MethodNotAllowed, - }; - } - - try - { - var id = apigProxyEvent.PathParameters["id"]; - - var product = await dataAccess.GetProduct(id); - - if (product == null) - { - return new APIGatewayProxyResponse - { - Body = "Not Found", - StatusCode = (int)HttpStatusCode.NotFound, - }; - } - - return new APIGatewayProxyResponse - { - StatusCode = (int)HttpStatusCode.OK, - Body = JsonSerializer.Serialize(product), - Headers = new Dictionary {{"Content-Type", "application/json"}} - }; - } - catch (Exception e) - { - context.Logger.LogLine($"Error getting product {e.Message} {e.StackTrace}"); - - return new APIGatewayProxyResponse - { - Body = "Not Found", - StatusCode = (int)HttpStatusCode.InternalServerError, - }; - } - } - } -} diff --git a/src/NET31-OTel/GetProduct/GetProduct.csproj b/src/NET31-OTel/GetProduct/GetProduct.csproj deleted file mode 100755 index 1e2954b..0000000 --- a/src/NET31-OTel/GetProduct/GetProduct.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - netcoreapp3.1 - true - - - - - - - - - - - - - diff --git a/src/NET31-OTel/GetProducts/Function.cs b/src/NET31-OTel/GetProducts/Function.cs deleted file mode 100755 index ec354d8..0000000 --- a/src/NET31-OTel/GetProducts/Function.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; - -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; -using OpenTelemetry.Contrib.Instrumentation.AWSLambda.Implementation; -using Shared; -using Shared.DataAccess; - -[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] - -namespace GetProducts -{ - public class Function - { - private readonly ProductsDAO dataAccess; - public Function() - { - Tracing.Init(); - this.dataAccess = new DynamoDbProducts(); - } - - public Task TracingFunctionHandler(APIGatewayProxyRequest request, ILambdaContext context) - { - return AWSLambdaWrapper.Trace(Tracing.TraceProvider, FunctionHandler, request, context); - } - - public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, - ILambdaContext context) - { - if (!apigProxyEvent.HttpMethod.Equals(HttpMethod.Get.Method)) - { - return new APIGatewayProxyResponse - { - Body = "Only GET allowed", - StatusCode = (int)HttpStatusCode.MethodNotAllowed, - }; - } - - context.Logger.LogLine($"Received {apigProxyEvent}"); - - var products = await dataAccess.GetAllProducts(); - - context.Logger.LogLine($"Found {products.Products.Count} product(s)"); - - return new APIGatewayProxyResponse - { - Body = JsonSerializer.Serialize(products), - StatusCode = 200, - Headers = new Dictionary {{"Content-Type", "application/json"}} - }; - } - } -} diff --git a/src/NET31-OTel/GetProducts/GetProducts.csproj b/src/NET31-OTel/GetProducts/GetProducts.csproj deleted file mode 100755 index 1e2954b..0000000 --- a/src/NET31-OTel/GetProducts/GetProducts.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - netcoreapp3.1 - true - - - - - - - - - - - - - diff --git a/src/NET31-OTel/PutProduct/Function.cs b/src/NET31-OTel/PutProduct/Function.cs deleted file mode 100755 index 3d09fc8..0000000 --- a/src/NET31-OTel/PutProduct/Function.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; - -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; -using OpenTelemetry.Contrib.Instrumentation.AWSLambda.Implementation; -using Shared; -using Shared.DataAccess; -using Shared.Models; - -[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] - -namespace PutProduct -{ - public class Function - { - private readonly ProductsDAO dataAccess; - public Function() - { - Tracing.Init(); - this.dataAccess = new DynamoDbProducts(); - } - - public Task TracingFunctionHandler(APIGatewayProxyRequest request, ILambdaContext context) - { - return AWSLambdaWrapper.Trace(Tracing.TraceProvider, FunctionHandler, request, context); - } - - public async Task FunctionHandler(APIGatewayProxyRequest apigProxyEvent, - ILambdaContext context) - { - if (!apigProxyEvent.HttpMethod.Equals(HttpMethod.Put.Method)) - { - return new APIGatewayProxyResponse - { - Body = "Only PUT allowed", - StatusCode = (int)HttpStatusCode.MethodNotAllowed, - }; - } - - try - { - var id = apigProxyEvent.PathParameters["id"]; - - var product = JsonSerializer.Deserialize(apigProxyEvent.Body); - - if (product == null || id != product.Id) - { - return new APIGatewayProxyResponse - { - Body = "Product ID in the body does not match path parameter", - StatusCode = (int)HttpStatusCode.BadRequest, - }; - } - - await dataAccess.PutProduct(product); - - return new APIGatewayProxyResponse - { - StatusCode = (int)HttpStatusCode.Created, - Body = $"Created product with id {id}" - }; - } - catch (Exception e) - { - context.Logger.LogLine($"Error creating product {e.Message} {e.StackTrace}"); - - return new APIGatewayProxyResponse - { - Body = "Not Found", - StatusCode = (int)HttpStatusCode.InternalServerError, - }; - } - } - } -} diff --git a/src/NET31-OTel/PutProduct/PutProduct.csproj b/src/NET31-OTel/PutProduct/PutProduct.csproj deleted file mode 100755 index 1e2954b..0000000 --- a/src/NET31-OTel/PutProduct/PutProduct.csproj +++ /dev/null @@ -1,18 +0,0 @@ - - - - netcoreapp3.1 - true - - - - - - - - - - - - - diff --git a/src/NET31-OTel/Shared/DataAccess/DynamoDbProducts.cs b/src/NET31-OTel/Shared/DataAccess/DynamoDbProducts.cs deleted file mode 100755 index 3aef009..0000000 --- a/src/NET31-OTel/Shared/DataAccess/DynamoDbProducts.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Amazon.DynamoDBv2; -using Amazon.DynamoDBv2.Model; -using Shared.Models; - -namespace Shared.DataAccess -{ - public class DynamoDbProducts : ProductsDAO - { - private static string PRODUCT_TABLE_NAME = Environment.GetEnvironmentVariable("PRODUCT_TABLE_NAME"); - private readonly AmazonDynamoDBClient _dynamoDbClient; - - public DynamoDbProducts() - { - this._dynamoDbClient = new AmazonDynamoDBClient(); - } - - public async Task GetProduct(string id) - { - var getItemResponse = await this._dynamoDbClient.GetItemAsync(new GetItemRequest(PRODUCT_TABLE_NAME, - new Dictionary(1) - { - {ProductMapper.PK, new AttributeValue(id)} - })); - - return getItemResponse.IsItemSet ? ProductMapper.ProductFromDynamoDB(getItemResponse.Item) : null; - } - - public async Task PutProduct(Product product) - { - await this._dynamoDbClient.PutItemAsync(PRODUCT_TABLE_NAME, ProductMapper.ProductToDynamoDb(product)); - } - - public async Task DeleteProduct(string id) - { - await this._dynamoDbClient.DeleteItemAsync(PRODUCT_TABLE_NAME, new Dictionary(1) - { - {ProductMapper.PK, new AttributeValue(id)} - }); - } - - public async Task GetAllProducts() - { - var data = await this._dynamoDbClient.ScanAsync(new ScanRequest() - { - TableName = PRODUCT_TABLE_NAME, - Limit = 20 - }); - - var products = new List(); - - foreach (var item in data.Items) - { - products.Add(ProductMapper.ProductFromDynamoDB(item)); - } - - return new ProductWrapper(products); - } - } -} \ No newline at end of file diff --git a/src/NET31-OTel/Shared/DataAccess/ProductMapper.cs b/src/NET31-OTel/Shared/DataAccess/ProductMapper.cs deleted file mode 100755 index 8e073ed..0000000 --- a/src/NET31-OTel/Shared/DataAccess/ProductMapper.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using Amazon.DynamoDBv2.Model; -using Shared.Models; - -namespace Shared.DataAccess -{ - public class ProductMapper - { - public static string PK = "id"; - public static string NAME = "name"; - public static string PRICE = "price"; - - public static Product ProductFromDynamoDB(Dictionary items) { - var product = new Product(items[PK].S, items[NAME].S, decimal.Parse(items[PRICE].N)); - - return product; - } - - public static Dictionary ProductToDynamoDb(Product product) { - Dictionary item = new Dictionary(3); - item.Add(PK, new AttributeValue(product.Id)); - item.Add(NAME, new AttributeValue(product.Name)); - item.Add(PRICE, new AttributeValue() - { - N = product.Price.ToString(CultureInfo.InvariantCulture) - }); - - return item; - } - } -} \ No newline at end of file diff --git a/src/NET31-OTel/Shared/DataAccess/ProductsDAO.cs b/src/NET31-OTel/Shared/DataAccess/ProductsDAO.cs deleted file mode 100755 index 3816e61..0000000 --- a/src/NET31-OTel/Shared/DataAccess/ProductsDAO.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Threading.Tasks; -using Shared.Models; - -namespace Shared.DataAccess -{ - public interface ProductsDAO - { - Task GetProduct(string id); - - Task PutProduct(Product product); - - Task DeleteProduct(string id); - - Task GetAllProducts(); - } -} \ No newline at end of file diff --git a/src/NET31-OTel/Shared/Models/Product.cs b/src/NET31-OTel/Shared/Models/Product.cs deleted file mode 100755 index 93f9896..0000000 --- a/src/NET31-OTel/Shared/Models/Product.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -namespace Shared.Models -{ - public class Product - { - public Product() - { - } - - public Product(string id, string name, decimal price) - { - this.Id = id; - this.Name = name; - this.Price = price; - } - - public string Id { get; set; } - - public string Name { get; set; } - - public decimal Price { get; private set; } - - public void SetPrice(decimal newPrice) - { - this.Price = Math.Round(newPrice, 2); - } - - public override string ToString() - { - return "Product{" + - "id='" + this.Id + '\'' + - ", name='" + this.Name + '\'' + - ", price=" + this.Price + - '}'; - } - } -} \ No newline at end of file diff --git a/src/NET31-OTel/Shared/Models/ProductWrapper.cs b/src/NET31-OTel/Shared/Models/ProductWrapper.cs deleted file mode 100755 index c89af93..0000000 --- a/src/NET31-OTel/Shared/Models/ProductWrapper.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; - -namespace Shared.Models -{ - public class ProductWrapper - { - public ProductWrapper(){} - - public ProductWrapper(List products) - { - this.Products = products; - } - - public List Products { get; set; } - } -} \ No newline at end of file diff --git a/src/NET31-OTel/Shared/Shared.csproj b/src/NET31-OTel/Shared/Shared.csproj deleted file mode 100755 index 18097c8..0000000 --- a/src/NET31-OTel/Shared/Shared.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - netstandard2.0 - - - - - - - - - - - - - - - - - - - - diff --git a/src/NET31-OTel/Shared/Tracing.cs b/src/NET31-OTel/Shared/Tracing.cs deleted file mode 100755 index 9147c3b..0000000 --- a/src/NET31-OTel/Shared/Tracing.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using OpenTelemetry; -using OpenTelemetry.Resources; -using OpenTelemetry.Metrics; -using OpenTelemetry.Trace; -using System.Diagnostics; -using System.Diagnostics.Metrics; -using OpenTelemetry.Contrib.Extensions.AWSXRay.Trace; - -namespace Shared -{ - public static class Tracing - { - public static TracerProvider TraceProvider; - - public static void Init() - { - AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); - - TraceProvider = Sdk.CreateTracerProviderBuilder() - .AddAWSInstrumentation() - .AddOtlpExporter() - .AddAWSLambdaConfigurations() - .Build(); - } - } -} \ No newline at end of file diff --git a/src/NET31-OTel/Shared/config.yaml b/src/NET31-OTel/Shared/config.yaml deleted file mode 100755 index 87c03f8..0000000 --- a/src/NET31-OTel/Shared/config.yaml +++ /dev/null @@ -1,25 +0,0 @@ -receivers: - otlp: - protocols: - grpc: - http: -processors: - batch: - timeout: 60s -exporters: - awsxray: - awsemf: - log_group_name: 'AOTLogGroup' - log_stream_name: 'AOTLogStream' - namespace: 'AOTMetricNS' - dimension_rollup_option: 1 -service: - pipelines: - traces: - receivers: [ otlp ] - exporters: [ awsxray ] - metrics: - receivers: [ otlp ] - exporters: [ awsemf ] - logs: - receivers: [ otlp ] \ No newline at end of file diff --git a/src/NET31-OTel/template.yaml b/src/NET31-OTel/template.yaml deleted file mode 100755 index 57d3653..0000000 --- a/src/NET31-OTel/template.yaml +++ /dev/null @@ -1,149 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" -Transform: AWS::Serverless-2016-10-31 - -Globals: - Function: - MemorySize: 1024 - Architectures: [!Ref LambdaArchitecture] - Runtime: dotnetcore3.1 - Timeout: 30 - Tracing: PassThrough - Environment: - Variables: - PRODUCT_TABLE_NAME: !Ref Table - Layers: - - !If [!Equals [ !Ref LambdaArchitecture, x86_64 ], arn:aws:lambda:eu-west-1:901920570463:layer:aws-otel-collector-amd64-ver-0-51-0:1, arn:aws:lambda:eu-west-1:901920570463:layer:aws-otel-collector-arm64-ver-0-51-0:1] - Api: - TracingEnabled: True - -Parameters: - LambdaArchitecture: - Type: String - AllowedValues: - - arm64 - - x86_64 - Description: Enter arm64 or x86_64 - -Resources: - GetProductsFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./GetProducts/ - Handler: GetProducts::GetProducts.Function::TracingFunctionHandler - Events: - Api: - Type: Api - Properties: - Path: / - Method: GET - Policies: - - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: dynamodb:Scan - Resource: !GetAtt Table.Arn - - GetProductFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./GetProduct/ - Handler: GetProduct::GetProduct.Function::TracingFunctionHandler - Events: - Api: - Type: Api - Properties: - Path: /{id} - Method: GET - Policies: - - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: dynamodb:GetItem - Resource: !GetAtt Table.Arn - - DeleteProductFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./DeleteProduct/ - Handler: DeleteProduct::DeleteProduct.Function::TracingFunctionHandler - Events: - Api: - Type: Api - Properties: - Path: /{id} - Method: DELETE - Policies: - - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - dynamodb:DeleteItem - - dynamodb:GetItem - Resource: !GetAtt Table.Arn - - PutProductFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./PutProduct/ - Handler: PutProduct::PutProduct.Function::TracingFunctionHandler - Events: - Api: - Type: Api - Properties: - Path: /{id} - Method: PUT - Policies: - - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: dynamodb:PutItem - Resource: !GetAtt Table.Arn - - GenerateLoadTestResults: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./GenerateLoadTestResults/ - Handler: GenerateLoadTestResults::GenerateLoadTestResults.Function::FunctionHandler - Runtime: dotnet6 - Events: - Api: - Type: HttpApi - Properties: - Path: /test-results - Method: GET - Environment: - Variables: - LOG_GROUP_PREFIX: !Sub "/aws/lambda/net-31-otel-" - LOAD_TEST_TYPE: "NET 3.1 with OTEL" - LAMBDA_ARCHITECTURE: !Ref LambdaArchitecture - Policies: - - Version: "2012-10-17" - Statement: - - Sid: AllowStartQueries - Effect: Allow - Action: - - logs:DescribeLogGroups - - logs:StartQuery - Resource: "*" - - Sid: AllowGetQueryResults - Effect: Allow - Action: logs:GetQueryResults - Resource: "*" - - Table: - Type: AWS::DynamoDB::Table - Properties: - AttributeDefinitions: - - AttributeName: id - AttributeType: S - BillingMode: PAY_PER_REQUEST - KeySchema: - - AttributeName: id - KeyType: HASH - StreamSpecification: - StreamViewType: NEW_AND_OLD_IMAGES - -Outputs: - ApiUrl: - Description: "API Gateway endpoint URL" - Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/" \ No newline at end of file diff --git a/src/NET31/DeleteProduct/DeleteProduct.csproj b/src/NET31/DeleteProduct/DeleteProduct.csproj deleted file mode 100755 index 15b8a00..0000000 --- a/src/NET31/DeleteProduct/DeleteProduct.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netcoreapp3.1 - true - - - - - - - - - - - - - - diff --git a/src/NET31/DeleteProduct/Function.cs b/src/NET31/DeleteProduct/Function.cs deleted file mode 100755 index 6c63732..0000000 --- a/src/NET31/DeleteProduct/Function.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; - -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; -using Shared.DataAccess; - -[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] - -namespace DeleteProduct -{ - public class Function - { - private readonly ProductsDAO dataAccess; - public Function() - { - this.dataAccess = new DynamoDbProducts(); - } - - public async Task FunctionHandler(APIGatewayHttpApiV2ProxyRequest apigProxyEvent, - ILambdaContext context) - { - if (!apigProxyEvent.RequestContext.Http.Method.Equals(HttpMethod.Delete.Method)) - { - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Only DELETE allowed", - StatusCode = (int)HttpStatusCode.MethodNotAllowed, - }; - } - - try - { - var id = apigProxyEvent.PathParameters["id"]; - - var product = await dataAccess.GetProduct(id); - - if (product == null) - { - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Not Found", - StatusCode = (int)HttpStatusCode.NotFound, - }; - } - - await dataAccess.DeleteProduct(product.Id); - - return new APIGatewayHttpApiV2ProxyResponse - { - StatusCode = (int)HttpStatusCode.OK, - Body = $"Product with id {id} deleted" - }; - } - catch (Exception e) - { - context.Logger.LogLine($"Error deleting product {e.Message} {e.StackTrace}"); - - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Not Found", - StatusCode = (int)HttpStatusCode.InternalServerError, - }; - } - } - } -} diff --git a/src/NET31/GenerateLoadTestResults/DateUtils.cs b/src/NET31/GenerateLoadTestResults/DateUtils.cs deleted file mode 100644 index c37c43d..0000000 --- a/src/NET31/GenerateLoadTestResults/DateUtils.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace GenerateLoadTestResults; - -public static class DateUtils -{ - public static long AsUnixTimestamp(this DateTime date) - { - DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); - TimeSpan diff = date.ToUniversalTime() - origin; - return (long)Math.Floor(diff.TotalSeconds); - } -} \ No newline at end of file diff --git a/src/NET31/GenerateLoadTestResults/Function.cs b/src/NET31/GenerateLoadTestResults/Function.cs deleted file mode 100644 index 21a57a8..0000000 --- a/src/NET31/GenerateLoadTestResults/Function.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; -using Amazon.CloudWatchLogs; -using Amazon.CloudWatchLogs.Model; -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; - -[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] - -namespace GenerateLoadTestResults -{ - public class Function - { - private AmazonCloudWatchLogsClient _cloudWatchLogsClient; - - public Function() - { - this._cloudWatchLogsClient = new AmazonCloudWatchLogsClient(); - } - - public async Task FunctionHandler( - APIGatewayHttpApiV2ProxyRequest apigProxyEvent, - ILambdaContext context) - { - if (!apigProxyEvent.RequestContext.Http.Method.Equals(HttpMethod.Get.Method)) - { - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Only GET allowed", - StatusCode = (int) HttpStatusCode.MethodNotAllowed, - }; - } - - try - { - var resultRows = 0; - var queryCount = 0; - - List> finalResults = new List>(); - - while (resultRows < 2 || queryCount >= 3) - { - finalResults = await runQuery(context); - - resultRows = finalResults.Count; - queryCount++; - } - - var wrapper = new QueryResultWrapper() - { - LoadTestType = - $"{Environment.GetEnvironmentVariable("LOAD_TEST_TYPE")} ({Environment.GetEnvironmentVariable("LAMBDA_ARCHITECTURE")})", - WarmStart = new QueryResult() - { - Count = finalResults[0][1].Value, - P50 = finalResults[0][2].Value, - P90 = finalResults[0][3].Value, - P99 = finalResults[0][4].Value, - Max = finalResults[0][5].Value, - }, - ColdStart = new QueryResult() - { - Count = finalResults[1][1].Value, - P50 = finalResults[1][2].Value, - P90 = finalResults[1][3].Value, - P99 = finalResults[1][4].Value, - Max = finalResults[1][5].Value, - } - }; - - return new APIGatewayHttpApiV2ProxyResponse - { - StatusCode = (int) HttpStatusCode.OK, - Body = wrapper.AsMarkdownTableRow(), - Headers = new Dictionary {{"Content-Type", "text/html"}} - }; - } - catch (Exception e) - { - context.Logger.LogLine($"Error retrieving results {e.Message} {e.StackTrace}"); - - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Not Found", - StatusCode = (int) HttpStatusCode.InternalServerError, - }; - } - } - - private async Task>> runQuery(ILambdaContext context) - { - var logGroupNamePrefix = - $"{Environment.GetEnvironmentVariable("LOG_GROUP_PREFIX")}{Environment.GetEnvironmentVariable("LAMBDA_ARCHITECTURE")}" - .Replace("_", "-"); - - context.Logger.LogLine($"Retrieving log groups with prefix {logGroupNamePrefix}"); - - var logGroupList = await _cloudWatchLogsClient.DescribeLogGroupsAsync(new DescribeLogGroupsRequest() - { - LogGroupNamePrefix = logGroupNamePrefix, - }); - - context.Logger.LogLine($"Found {logGroupList.LogGroups.Count} log group(s)"); - - var queryRes = await _cloudWatchLogsClient.StartQueryAsync(new StartQueryRequest() - { - LogGroupNames = logGroupList.LogGroups.Select(p => p.LogGroupName).ToList(), - QueryString = - "filter @type=\"REPORT\" | fields greatest(@initDuration, 0) + @duration as duration, ispresent(@initDuration) as coldstart | stats count(*) as count, pct(duration, 50) as p50, pct(duration, 90) as p90, pct(duration, 99) as p99, max(duration) as max by coldstart", - StartTime = DateTime.Now.AddMinutes(-20).AsUnixTimestamp(), - EndTime = DateTime.Now.AsUnixTimestamp(), - }); - - context.Logger.LogLine($"Running query, query id is {queryRes.QueryId}"); - - QueryStatus currentQueryStatus = QueryStatus.Running; - List> finalResults = new List>(); - - while (currentQueryStatus == QueryStatus.Running || currentQueryStatus == QueryStatus.Scheduled) - { - context.Logger.LogLine("Retrieving query results"); - - var queryResults = await _cloudWatchLogsClient.GetQueryResultsAsync(new GetQueryResultsRequest() - { - QueryId = queryRes.QueryId - }); - - context.Logger.LogLine($"Query result status is {queryResults.Status}"); - - currentQueryStatus = queryResults.Status; - finalResults = queryResults.Results; - - await Task.Delay(TimeSpan.FromSeconds(5)); - } - - context.Logger.LogLine($"Final results: {finalResults.Count} row(s)"); - - return finalResults; - } - } -} \ No newline at end of file diff --git a/src/NET31/GenerateLoadTestResults/GenerateLoadTestResults.csproj b/src/NET31/GenerateLoadTestResults/GenerateLoadTestResults.csproj deleted file mode 100644 index 4a8d708..0000000 --- a/src/NET31/GenerateLoadTestResults/GenerateLoadTestResults.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - net6.0 - true - true - - - - - - - - - - - diff --git a/src/NET31/GenerateLoadTestResults/QueryResult.cs b/src/NET31/GenerateLoadTestResults/QueryResult.cs deleted file mode 100644 index 69b5a31..0000000 --- a/src/NET31/GenerateLoadTestResults/QueryResult.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace GenerateLoadTestResults; - -public record QueryResultWrapper -{ - public string LoadTestType { get; set; } - - public QueryResult ColdStart { get; set; } - - public QueryResult WarmStart { get; set; } - - public string AsMarkdownTableRow() => $"
Cold Start (ms)Warm Start (ms)
p50p90p99maxp50p90p99max
{LoadTestType}{ColdStart.P50}{ColdStart.P90}{ColdStart.P99}{ColdStart.Max}{WarmStart.P50}{WarmStart.P90}{WarmStart.P99}{WarmStart.Max}
"; -} - -public record QueryResult -{ - public string Count { get; set; } - - public string P50 { get; set; } - - public string P90 { get; set; } - - public string P99 { get; set; } - - public string Max { get; set; } -} \ No newline at end of file diff --git a/src/NET31/GetProduct/Function.cs b/src/NET31/GetProduct/Function.cs deleted file mode 100755 index 5201bc9..0000000 --- a/src/NET31/GetProduct/Function.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; - -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; -using Shared.DataAccess; - -[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] - -namespace GetProduct -{ - public class Function - { - private readonly ProductsDAO dataAccess; - public Function() - { - this.dataAccess = new DynamoDbProducts(); - } - - public async Task FunctionHandler(APIGatewayHttpApiV2ProxyRequest apigProxyEvent, - ILambdaContext context) - { - if (!apigProxyEvent.RequestContext.Http.Method.Equals(HttpMethod.Get.Method)) - { - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Only GET allowed", - StatusCode = (int)HttpStatusCode.MethodNotAllowed, - }; - } - - try - { - var id = apigProxyEvent.PathParameters["id"]; - - var product = await dataAccess.GetProduct(id); - - if (product == null) - { - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Not Found", - StatusCode = (int)HttpStatusCode.NotFound, - }; - } - - return new APIGatewayHttpApiV2ProxyResponse - { - StatusCode = (int)HttpStatusCode.OK, - Body = JsonSerializer.Serialize(product), - Headers = new Dictionary {{"Content-Type", "application/json"}} - }; - } - catch (Exception e) - { - context.Logger.LogLine($"Error getting product {e.Message} {e.StackTrace}"); - - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Not Found", - StatusCode = (int)HttpStatusCode.InternalServerError, - }; - } - } - } -} diff --git a/src/NET31/GetProduct/GetProduct.csproj b/src/NET31/GetProduct/GetProduct.csproj deleted file mode 100755 index 15b8a00..0000000 --- a/src/NET31/GetProduct/GetProduct.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netcoreapp3.1 - true - - - - - - - - - - - - - - diff --git a/src/NET31/GetProducts/Function.cs b/src/NET31/GetProducts/Function.cs deleted file mode 100755 index 9fd2205..0000000 --- a/src/NET31/GetProducts/Function.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; - -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; -using Shared.DataAccess; - -[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] - -namespace GetProducts -{ - public class Function - { - private readonly ProductsDAO dataAccess; - public Function() - { - this.dataAccess = new DynamoDbProducts(); - } - - public async Task FunctionHandler(APIGatewayHttpApiV2ProxyRequest apigProxyEvent, - ILambdaContext context) - { - if (!apigProxyEvent.RequestContext.Http.Method.Equals(HttpMethod.Get.Method)) - { - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Only GET allowed", - StatusCode = (int)HttpStatusCode.MethodNotAllowed, - }; - } - - context.Logger.LogLine($"Received {apigProxyEvent}"); - - var products = await dataAccess.GetAllProducts(); - - context.Logger.LogLine($"Found {products.Products.Count} product(s)"); - - return new APIGatewayHttpApiV2ProxyResponse - { - Body = JsonSerializer.Serialize(products), - StatusCode = 200, - Headers = new Dictionary {{"Content-Type", "application/json"}} - }; - } - } -} diff --git a/src/NET31/GetProducts/GetProducts.csproj b/src/NET31/GetProducts/GetProducts.csproj deleted file mode 100755 index 15b8a00..0000000 --- a/src/NET31/GetProducts/GetProducts.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netcoreapp3.1 - true - - - - - - - - - - - - - - diff --git a/src/NET31/PutProduct/Function.cs b/src/NET31/PutProduct/Function.cs deleted file mode 100755 index 43e2a1f..0000000 --- a/src/NET31/PutProduct/Function.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Text.Json; -using System.Threading.Tasks; - -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; -using Shared.DataAccess; -using Shared.Models; - -[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] - -namespace PutProduct -{ - public class Function - { - private readonly ProductsDAO dataAccess; - public Function() - { - this.dataAccess = new DynamoDbProducts(); - } - - public async Task FunctionHandler(APIGatewayHttpApiV2ProxyRequest apigProxyEvent, - ILambdaContext context) - { - if (!apigProxyEvent.RequestContext.Http.Method.Equals(HttpMethod.Put.Method)) - { - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Only PUT allowed", - StatusCode = (int)HttpStatusCode.MethodNotAllowed, - }; - } - - try - { - var id = apigProxyEvent.PathParameters["id"]; - - var product = JsonSerializer.Deserialize(apigProxyEvent.Body); - - if (product == null || id != product.Id) - { - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Product ID in the body does not match path parameter", - StatusCode = (int)HttpStatusCode.BadRequest, - }; - } - - await dataAccess.PutProduct(product); - - return new APIGatewayHttpApiV2ProxyResponse - { - StatusCode = (int)HttpStatusCode.Created, - Body = $"Created product with id {id}" - }; - } - catch (Exception e) - { - context.Logger.LogLine($"Error creating product {e.Message} {e.StackTrace}"); - - return new APIGatewayHttpApiV2ProxyResponse - { - Body = "Not Found", - StatusCode = (int)HttpStatusCode.InternalServerError, - }; - } - } - } -} diff --git a/src/NET31/PutProduct/PutProduct.csproj b/src/NET31/PutProduct/PutProduct.csproj deleted file mode 100755 index 15b8a00..0000000 --- a/src/NET31/PutProduct/PutProduct.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - netcoreapp3.1 - true - - - - - - - - - - - - - - diff --git a/src/NET31/Shared/DataAccess/DynamoDbProducts.cs b/src/NET31/Shared/DataAccess/DynamoDbProducts.cs deleted file mode 100755 index 3aef009..0000000 --- a/src/NET31/Shared/DataAccess/DynamoDbProducts.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Amazon.DynamoDBv2; -using Amazon.DynamoDBv2.Model; -using Shared.Models; - -namespace Shared.DataAccess -{ - public class DynamoDbProducts : ProductsDAO - { - private static string PRODUCT_TABLE_NAME = Environment.GetEnvironmentVariable("PRODUCT_TABLE_NAME"); - private readonly AmazonDynamoDBClient _dynamoDbClient; - - public DynamoDbProducts() - { - this._dynamoDbClient = new AmazonDynamoDBClient(); - } - - public async Task GetProduct(string id) - { - var getItemResponse = await this._dynamoDbClient.GetItemAsync(new GetItemRequest(PRODUCT_TABLE_NAME, - new Dictionary(1) - { - {ProductMapper.PK, new AttributeValue(id)} - })); - - return getItemResponse.IsItemSet ? ProductMapper.ProductFromDynamoDB(getItemResponse.Item) : null; - } - - public async Task PutProduct(Product product) - { - await this._dynamoDbClient.PutItemAsync(PRODUCT_TABLE_NAME, ProductMapper.ProductToDynamoDb(product)); - } - - public async Task DeleteProduct(string id) - { - await this._dynamoDbClient.DeleteItemAsync(PRODUCT_TABLE_NAME, new Dictionary(1) - { - {ProductMapper.PK, new AttributeValue(id)} - }); - } - - public async Task GetAllProducts() - { - var data = await this._dynamoDbClient.ScanAsync(new ScanRequest() - { - TableName = PRODUCT_TABLE_NAME, - Limit = 20 - }); - - var products = new List(); - - foreach (var item in data.Items) - { - products.Add(ProductMapper.ProductFromDynamoDB(item)); - } - - return new ProductWrapper(products); - } - } -} \ No newline at end of file diff --git a/src/NET31/Shared/DataAccess/ProductMapper.cs b/src/NET31/Shared/DataAccess/ProductMapper.cs deleted file mode 100755 index 8e073ed..0000000 --- a/src/NET31/Shared/DataAccess/ProductMapper.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using Amazon.DynamoDBv2.Model; -using Shared.Models; - -namespace Shared.DataAccess -{ - public class ProductMapper - { - public static string PK = "id"; - public static string NAME = "name"; - public static string PRICE = "price"; - - public static Product ProductFromDynamoDB(Dictionary items) { - var product = new Product(items[PK].S, items[NAME].S, decimal.Parse(items[PRICE].N)); - - return product; - } - - public static Dictionary ProductToDynamoDb(Product product) { - Dictionary item = new Dictionary(3); - item.Add(PK, new AttributeValue(product.Id)); - item.Add(NAME, new AttributeValue(product.Name)); - item.Add(PRICE, new AttributeValue() - { - N = product.Price.ToString(CultureInfo.InvariantCulture) - }); - - return item; - } - } -} \ No newline at end of file diff --git a/src/NET31/Shared/DataAccess/ProductsDAO.cs b/src/NET31/Shared/DataAccess/ProductsDAO.cs deleted file mode 100755 index 3816e61..0000000 --- a/src/NET31/Shared/DataAccess/ProductsDAO.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Threading.Tasks; -using Shared.Models; - -namespace Shared.DataAccess -{ - public interface ProductsDAO - { - Task GetProduct(string id); - - Task PutProduct(Product product); - - Task DeleteProduct(string id); - - Task GetAllProducts(); - } -} \ No newline at end of file diff --git a/src/NET31/Shared/Models/Product.cs b/src/NET31/Shared/Models/Product.cs deleted file mode 100755 index 93f9896..0000000 --- a/src/NET31/Shared/Models/Product.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; - -namespace Shared.Models -{ - public class Product - { - public Product() - { - } - - public Product(string id, string name, decimal price) - { - this.Id = id; - this.Name = name; - this.Price = price; - } - - public string Id { get; set; } - - public string Name { get; set; } - - public decimal Price { get; private set; } - - public void SetPrice(decimal newPrice) - { - this.Price = Math.Round(newPrice, 2); - } - - public override string ToString() - { - return "Product{" + - "id='" + this.Id + '\'' + - ", name='" + this.Name + '\'' + - ", price=" + this.Price + - '}'; - } - } -} \ No newline at end of file diff --git a/src/NET31/Shared/Models/ProductWrapper.cs b/src/NET31/Shared/Models/ProductWrapper.cs deleted file mode 100755 index c89af93..0000000 --- a/src/NET31/Shared/Models/ProductWrapper.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; - -namespace Shared.Models -{ - public class ProductWrapper - { - public ProductWrapper(){} - - public ProductWrapper(List products) - { - this.Products = products; - } - - public List Products { get; set; } - } -} \ No newline at end of file diff --git a/src/NET31/Shared/Shared.csproj b/src/NET31/Shared/Shared.csproj deleted file mode 100755 index 61e50b2..0000000 --- a/src/NET31/Shared/Shared.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - - netstandard2.0 - - - - - - - diff --git a/src/NET31/template.yaml b/src/NET31/template.yaml deleted file mode 100755 index 5272ee6..0000000 --- a/src/NET31/template.yaml +++ /dev/null @@ -1,145 +0,0 @@ -AWSTemplateFormatVersion: "2010-09-09" -Transform: AWS::Serverless-2016-10-31 - -Globals: - Function: - MemorySize: 1024 - Architectures: [!Ref LambdaArchitecture] - Runtime: dotnetcore3.1 - Timeout: 30 - Tracing: Active - Environment: - Variables: - PRODUCT_TABLE_NAME: !Ref Table - -Parameters: - LambdaArchitecture: - Type: String - AllowedValues: - - arm64 - - x86_64 - Description: Enter arm64 or x86_64 - -Resources: - GetProductsFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./GetProducts/ - Handler: GetProducts::GetProducts.Function::FunctionHandler - Events: - Api: - Type: HttpApi - Properties: - Path: / - Method: GET - Policies: - - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: dynamodb:Scan - Resource: !GetAtt Table.Arn - - GetProductFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./GetProduct/ - Handler: GetProduct::GetProduct.Function::FunctionHandler - Events: - Api: - Type: HttpApi - Properties: - Path: /{id} - Method: GET - Policies: - - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: dynamodb:GetItem - Resource: !GetAtt Table.Arn - - DeleteProductFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./DeleteProduct/ - Handler: DeleteProduct::DeleteProduct.Function::FunctionHandler - Events: - Api: - Type: HttpApi - Properties: - Path: /{id} - Method: DELETE - Policies: - - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: - - dynamodb:DeleteItem - - dynamodb:GetItem - Resource: !GetAtt Table.Arn - - PutProductFunction: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./PutProduct/ - Handler: PutProduct::PutProduct.Function::FunctionHandler - Events: - Api: - Type: HttpApi - Properties: - Path: /{id} - Method: PUT - Policies: - - Version: "2012-10-17" - Statement: - - Effect: Allow - Action: dynamodb:PutItem - Resource: !GetAtt Table.Arn - - GenerateLoadTestResults: - Type: AWS::Serverless::Function - Properties: - CodeUri: ./GenerateLoadTestResults/ - Handler: GenerateLoadTestResults::GenerateLoadTestResults.Function::FunctionHandler - Runtime: dotnet6 - Events: - Api: - Type: HttpApi - Properties: - Path: /test-results - Method: GET - Environment: - Variables: - LOG_GROUP_PREFIX: !Sub "/aws/lambda/net-31-base-" - LOAD_TEST_TYPE: "NET 3.1" - LAMBDA_ARCHITECTURE: !Ref LambdaArchitecture - Policies: - - Version: "2012-10-17" - Statement: - - Sid: AllowStartQueries - Effect: Allow - Action: - - logs:DescribeLogGroups - - logs:StartQuery - Resource: "*" - - Sid: AllowGetQueryResults - Effect: Allow - Action: logs:GetQueryResults - Resource: "*" - - Table: - Type: AWS::DynamoDB::Table - Properties: - AttributeDefinitions: - - AttributeName: id - AttributeType: S - BillingMode: PAY_PER_REQUEST - KeySchema: - - AttributeName: id - KeyType: HASH - StreamSpecification: - StreamViewType: NEW_AND_OLD_IMAGES - -Outputs: - ApiUrl: - Description: "API Gateway endpoint URL" - Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/" \ No newline at end of file