Skip to content

Commit

Permalink
Merge pull request #508 from aws-powertools/develop
Browse files Browse the repository at this point in the history
chore: Sync main with develop for release 1.8.1
  • Loading branch information
hjgraca authored Oct 30, 2023
2 parents 820c44e + bfa0ded commit cf7302a
Show file tree
Hide file tree
Showing 24 changed files with 527 additions and 51 deletions.
3 changes: 3 additions & 0 deletions docs/core/metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,9 @@ You can add high-cardinality data as part of your Metrics log with `AddMetadata`
!!! info
**This will not be available during metrics visualization** - Use **dimensions** for this purpose

!!! info
Adding metadata with a key that is the same as an existing metric will be ignored

=== "Function.cs"

```csharp hl_lines="9"
Expand Down
57 changes: 51 additions & 6 deletions libraries/src/AWS.Lambda.Powertools.BatchProcessing/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,59 @@
# AWS.Lambda.Powertools.BatchProcessing
...

The batch processing utility handles partial failures when processing batches from Amazon SQS, Amazon Kinesis Data Streams, and Amazon DynamoDB Streams.

## Key features
...

* Reports batch item failures to reduce number of retries for a record upon errors
* Simple interface to process each batch record
* Bring your own batch processor
* Parallel processing

## Background

When using SQS, Kinesis Data Streams, or DynamoDB Streams as a Lambda event source, your Lambda functions are triggered with a batch of messages.

If your function fails to process any message from the batch, the entire batch returns to your queue or stream. This same batch is then retried until either condition happens first: a) your Lambda function returns a successful response, b) record reaches maximum retry attempts, or c) when records expire.

This behavior changes when you enable Report Batch Item Failures feature in your Lambda function event source configuration:

* [SQS queues](https://docs.powertools.aws.dev/lambda/dotnet/utilities/batch-processing/#sqs-standard). Only messages reported as failure will return to the queue for a retry, while successful ones will be deleted.
* [Kinesis data streams](https://docs.powertools.aws.dev/lambda/dotnet/utilities/batch-processing/#kinesis-and-dynamodb-streams) and [DynamoDB streams](https://docs.powertools.aws.dev/lambda/dotnet/utilities/batch-processing/#kinesis-and-dynamodb-streams). Single reported failure will use its sequence number as the stream checkpoint. Multiple reported failures will use the lowest sequence number as checkpoint.

## Read the docs
...

For a full list of features go to [docs.powertools.aws.dev/lambda/dotnet/utilities/batch-processing/](https://docs.powertools.aws.dev/lambda/dotnet/utilities/batch-processing/)

GitHub: https://github.com/aws-powertools/powertools-lambda-dotnet/

## Sample Function
...

## Sample output
...
View the full example here: [github.com/aws-powertools/powertools-lambda-dotnet/tree/develop/examples/BatchProcessing](https://github.com/aws-powertools/powertools-lambda-dotnet/tree/develop/examples/BatchProcessing)

```csharp
[BatchProcessor(RecordHandler = typeof(CustomSqsRecordHandler))]
public BatchItemFailuresResponse HandlerUsingAttribute(SQSEvent _)
{
return SqsBatchProcessor.Result.BatchItemFailuresResponse;
}

public class CustomSqsRecordHandler : ISqsRecordHandler
{
public async Task<RecordHandlerResult> HandleAsync(SQSEvent.SQSMessage record, CancellationToken cancellationToken)
{
/*
Your business logic.
If an exception is thrown, the item will be marked as a partial batch item failure.
*/

var product = JsonSerializer.Deserialize<JsonElement>(record.Body);

if (product.GetProperty("Id").GetInt16() == 4)
{
throw new ArgumentException("Error on 4");
}

return await Task.FromResult(RecordHandlerResult.None);
}
}
```
2 changes: 2 additions & 0 deletions libraries/src/AWS.Lambda.Powertools.Common/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# AWS.Lambda.Powertools.Common

Powertools for AWS Lambda (.NET) Common library

### As of release 1.7.0 of Powertools for AWS Lambda (.NET) this package is no longer required. For that reason It’s being deprecated and is no longer maintained.
27 changes: 24 additions & 3 deletions libraries/src/AWS.Lambda.Powertools.Idempotency/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ times with the same parameters**. This makes idempotent operations safe to retry

* Prevent Lambda handler function from executing more than once on the same event payload during a time window
* Ensure Lambda handler returns the same result when called with the same payload
* Select a subset of the event as the idempotency key using JMESPath expressions
* Select a subset of the event as the idempotency key using [JMESPath](https://jmespath.org/) expressions
* Set a time window in which records with the same payload should be considered duplicates
* Expires in-progress executions if the Lambda function times out halfway through

## Read the docs

For a full list of features go to [docs.powertools.aws.dev/lambda/dotnet/utilities/idempotency/](https://docs.powertools.aws.dev/lambda/dotnet/utilities/idempotency/)

GitHub: https://github.com/aws-powertools/powertools-lambda-dotnet/

## Installation
You should install with NuGet:
Expand All @@ -35,5 +41,20 @@ Or via the .NET Core command line interface:
dotnet add package Amazon.Lambda.PowerTools.Idempotency
```

## Acknowledgment
This project has been ported from the Java Idempotency PowerTool Utility
## Sample Function

```csharp
public class Function
{
public Function()
{
Idempotency.Configure(builder => builder.UseDynamoDb("idempotency_table"));
}

[Idempotent]
public Task<string> FunctionHandler(string input, ILambdaContext context)
{
return Task.FromResult(input.ToUpper());
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace AWS.Lambda.Powertools.Logging.Internal.Converters;

/// <summary>
/// DateOnly JSON converter
/// </summary>
public class DateOnlyConverter : JsonConverter<DateOnly>
{
private const string DateFormat = "yyyy-MM-dd";

/// <summary>
/// Converts DateOnly from JSON.
/// </summary>
/// <param name="reader"></param>
/// <param name="typeToConvert"></param>
/// <param name="options"></param>
/// <returns></returns>
public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateOnly.ParseExact(reader.GetString()!, DateFormat, CultureInfo.InvariantCulture);
}

/// <summary>
/// Converts DateOnly to JSON.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
/// <param name="options"></param>
public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString(DateFormat, CultureInfo.InvariantCulture));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace AWS.Lambda.Powertools.Logging.Internal.Converters;

/// <summary>
/// TimeOnly JSON converter
/// </summary>
internal class TimeOnlyConverter : JsonConverter<TimeOnly>
{
private const string TimeFormat = "HH:mm:ss.FFFFFFF";

/// <summary>
/// Converts TimeOnly from JSON.
/// </summary>
/// <param name="reader"></param>
/// <param name="typeToConvert"></param>
/// <param name="options"></param>
/// <returns></returns>
public override TimeOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return TimeOnly.ParseExact(reader.GetString()!, TimeFormat, CultureInfo.InvariantCulture);
}

/// <summary>
/// Converts TimeOnly to JSON.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
/// <param name="options"></param>
public override void Write(Utf8JsonWriter writer, TimeOnly value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString(TimeFormat, CultureInfo.InvariantCulture));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ private static JsonSerializerOptions BuildJsonSerializerOptions()
jsonOptions.Converters.Add(new ExceptionConverter());
jsonOptions.Converters.Add(new MemoryStreamConverter());
jsonOptions.Converters.Add(new ConstantClassConverter());
jsonOptions.Converters.Add(new DateOnlyConverter());
jsonOptions.Converters.Add(new TimeOnlyConverter());
return jsonOptions;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,8 @@ private JsonSerializerOptions BuildJsonSerializerOptions()
jsonOptions.Converters.Add(new ExceptionConverter());
jsonOptions.Converters.Add(new MemoryStreamConverter());
jsonOptions.Converters.Add(new ConstantClassConverter());
jsonOptions.Converters.Add(new DateOnlyConverter());
jsonOptions.Converters.Add(new TimeOnlyConverter());

jsonOptions.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;

Expand Down
2 changes: 1 addition & 1 deletion libraries/src/AWS.Lambda.Powertools.Logging/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ The logging utility provides a [AWS Lambda](https://aws.amazon.com/lambda/) opti

## Read the docs

For a full list of features go to [docs.powertools.aws.dev/lambda/dotnet/core/logging/](docs.powertools.aws.dev/lambda/dotnet/core/logging/)
For a full list of features go to [docs.powertools.aws.dev/lambda/dotnet/core/logging/](https://docs.powertools.aws.dev/lambda/dotnet/core/logging/)

GitHub: https://github.com/aws-powertools/powertools-lambda-dotnet/

Expand Down
2 changes: 1 addition & 1 deletion libraries/src/AWS.Lambda.Powertools.Metrics/IMetrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace AWS.Lambda.Powertools.Metrics;
/// Implements the <see cref="System.IDisposable" />
/// </summary>
/// <seealso cref="System.IDisposable" />
public interface IMetrics : IDisposable
public interface IMetrics
{
/// <summary>
/// Adds metric
Expand Down
52 changes: 33 additions & 19 deletions libraries/src/AWS.Lambda.Powertools.Metrics/Metrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ namespace AWS.Lambda.Powertools.Metrics;
/// Implements the <see cref="IMetrics" />
/// </summary>
/// <seealso cref="IMetrics" />
public class Metrics : IMetrics
public class Metrics : IMetrics, IDisposable
{
/// <summary>
/// The instance
/// </summary>
private static IMetrics _instance;

/// <summary>
/// The context
/// </summary>
Expand Down Expand Up @@ -65,15 +65,15 @@ public class Metrics : IMetrics
internal Metrics(IPowertoolsConfigurations powertoolsConfigurations, string nameSpace = null, string service = null,
bool raiseOnEmptyMetrics = false, bool captureColdStartEnabled = false)
{
if (_instance != null) return;
_instance ??= this;

_instance = this;
_powertoolsConfigurations = powertoolsConfigurations;
_raiseOnEmptyMetrics = raiseOnEmptyMetrics;
_captureColdStartEnabled = captureColdStartEnabled;
_context = InitializeContext(nameSpace, service, null);

_powertoolsConfigurations.SetExecutionEnvironment(this);

}

/// <summary>
Expand All @@ -91,11 +91,11 @@ void IMetrics.AddMetric(string key, double value, MetricUnit unit, MetricResolut
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(
$"'AddMetric' method requires a valid metrics key. 'Null' or empty values are not allowed.");
nameof(key), "'AddMetric' method requires a valid metrics key. 'Null' or empty values are not allowed.");

if (value < 0) {
throw new ArgumentException(
"'AddMetric' method requires a valid metrics value. Value must be >= 0.");
"'AddMetric' method requires a valid metrics value. Value must be >= 0.", nameof(value));
}

var metrics = _context.GetMetrics();
Expand Down Expand Up @@ -150,8 +150,8 @@ string IMetrics.GetService()
void IMetrics.AddDimension(string key, string value)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(
$"'AddDimension' method requires a valid dimension key. 'Null' or empty values are not allowed.");
throw new ArgumentNullException(nameof(key),
"'AddDimension' method requires a valid dimension key. 'Null' or empty values are not allowed.");

_context.AddDimension(key, value);
}
Expand All @@ -168,28 +168,28 @@ void IMetrics.AddDimension(string key, string value)
void IMetrics.AddMetadata(string key, object value)
{
if (string.IsNullOrWhiteSpace(key))
throw new ArgumentNullException(
$"'AddMetadata' method requires a valid metadata key. 'Null' or empty values are not allowed.");
throw new ArgumentNullException(nameof(key),
"'AddMetadata' method requires a valid metadata key. 'Null' or empty values are not allowed.");

_context.AddMetadata(key, value);
}

/// <summary>
/// Implements interface that sets default dimension list
/// </summary>
/// <param name="defaultDimensions">Default Dimension List</param>
/// <param name="defaultDimension">Default Dimension List</param>
/// <exception cref="System.ArgumentNullException">
/// 'SetDefaultDimensions' method requires a valid key pair. 'Null' or empty
/// values are not allowed.
/// </exception>
void IMetrics.SetDefaultDimensions(Dictionary<string, string> defaultDimensions)
void IMetrics.SetDefaultDimensions(Dictionary<string, string> defaultDimension)
{
foreach (var item in defaultDimensions)
foreach (var item in defaultDimension)
if (string.IsNullOrWhiteSpace(item.Key) || string.IsNullOrWhiteSpace(item.Value))
throw new ArgumentNullException(
$"'SetDefaultDimensions' method requires a valid key pair. 'Null' or empty values are not allowed.");
throw new ArgumentNullException(nameof(item.Key),
"'SetDefaultDimensions' method requires a valid key pair. 'Null' or empty values are not allowed.");

_context.SetDefaultDimensions(DictionaryToList(defaultDimensions));
_context.SetDefaultDimensions(DictionaryToList(defaultDimension));
}

/// <summary>
Expand Down Expand Up @@ -258,8 +258,8 @@ void IMetrics.PushSingleMetric(string metricName, double value, MetricUnit unit,
Dictionary<string, string> defaultDimensions, MetricResolution metricResolution)
{
if (string.IsNullOrWhiteSpace(metricName))
throw new ArgumentNullException(
$"'PushSingleMetric' method requires a valid metrics key. 'Null' or empty values are not allowed.");
throw new ArgumentNullException(nameof(metricName),
"'PushSingleMetric' method requires a valid metrics key. 'Null' or empty values are not allowed.");

using var context = InitializeContext(nameSpace, service, defaultDimensions);
context.AddMetric(metricName, value, unit, metricResolution);
Expand All @@ -272,7 +272,21 @@ void IMetrics.PushSingleMetric(string metricName, double value, MetricUnit unit,
/// </summary>
public void Dispose()
{
_instance.Flush();
Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
///
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
// Cleanup
if (disposing)
{
_instance.Flush();
}
}

/// <summary>
Expand Down
Loading

0 comments on commit cf7302a

Please sign in to comment.