Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix DynamoDbv2 bug for incorrect DateTime epoch serialization when date falls out of epoch supported range #3672

Open
wants to merge 9 commits into
base: main-staging
Choose a base branch
from

Conversation

ashishdhingra
Copy link
Contributor

@ashishdhingra ashishdhingra commented Feb 25, 2025

Description

Taking over the PR #3442 to get it across finish line. @sander1095 Thanks for the initial PR.

NOTE (for reviewer):

  • Added AWSSSDKUtils.ConvertFromUnixEpochSeconds(long) to convert the stored epoch seconds string back to DateTime. Please review for it's correctness.
  • Please use commit df4d78d1d429daf6f37a1b9f9025da87d19290f0 to see the changes made on top of customer's changes.

IMPORTANT: Per @boblodgett, there were some UTC DateTime fix implemented in V4. So we need to make sure we don't overwrite these when merging to v4-development branch.

Motivation and Context

#3442

Testing

Dry run DRY_RUN-3c72ed89-be29-4254-9bd1-299cbc34e081 completed successfully.

Screenshots (if appropriate)

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • My code follows the code style of this project
  • My change requires a change to the documentation
  • I have updated the documentation accordingly
  • I have read the README document
  • I have added tests to cover my changes
  • All new and existing tests passed

License

  • I confirm that this pull request can be released under the Apache 2 license


if (dateTime.HasValue)
{
entry = (Primitive)(dateTime.Value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we want to modify the caller's reference of entry shouldn't we do something like:

entry  = new Primitive(datetime.Value)

Aren't we just modifying the local copy of the reference here? I see that being done below, but maybe you forgot that here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was copied from the existing EpochSecondsToDateTime.

Yes, we are just modifying the local copy of the reference here and then returning it. entry = new Primitive(datetime.Value) would also the same, but in a more elegant way. 😄

The function DateTimeToEpochSecondsLong() was added by customer which uses new Primitive(epochSecondsAsString, saveAsNumeric: true), so didn't touched it.

@@ -822,7 +835,7 @@ private static void PopulateConfigFromType(ItemStorageConfig config, Type type)
}
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: added whitespace?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed it somehow.


// To prevent the data from being (de)serialized incorrectly, we choose to throw the exception instead
// of swallowing it.
throw;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should throw our own exception calling out our failure to convert the DateTime to a Epoch. Provide the value of the DateTime, attributeName, and the original exception as the inner exception. If we don't users will get a random numeric conversion exception and have no context why they are getting the error and how to fix it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Please review.

"Encountered error attempting to convert '{0}' with value '{1}' to epoch seconds: {1}",
attributeName, entry, e);

// To prevent the data from being (de)serialized incorrectly, we choose to throw the exception instead
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For historical purposes can you call out in the comment this was a purposeful change from the existing DateTimeToEpochSeconds to avoid issues silently fallback to string format DateTime.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

throw;
}

if (epochSecondsAsString != null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can never be false anymore since we are always throwing an exception if we fail to convert. To avoid the code getting refactored in weird ways in the future I would add line 336 right after the ConvertToUnixEpochSecondsString call and then return. That would leave no code after the catch block and we don't need the extra if check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Please review.

@@ -346,6 +354,7 @@ public void TestContext_CustomDateTimeConverter(bool retrieveDateTimeInUtc)
Context.ConverterCache.Add(typeof(DateTime), new DateTimeUtcConverter());

var currTime = DateTime.Now;
var longEpochTime = new DateTime(2039, 2, 5, 17, 49, 55, DateTimeKind.Local); // DateTime.Now returns Local kind.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test that uses a date before 1970 the start of the Unix epoch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used var currTimeBefore1970 = new DateTime(1969, 6, 12, 4, 56, 43, DateTimeKind.Local); for attribute EpochDate2 (it was somehow not decorated with StoreAsEpoch. Decorated it with StoreAsEpoc.

Please advise if we need to also test it with StoreAsEpochLong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@@ -0,0 +1,11 @@
{
"services": [
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also changed Core (sdk/src/Core/Amazon.Util/AWSSDKUtils.cs) but didn't include it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching it. Fixed.

@ashishdhingra ashishdhingra force-pushed the PR-3442-DynamoDB-DateTimeEpoch branch from 9ea348c to 5eba8fd Compare February 27, 2025 15:26
@ashishdhingra ashishdhingra force-pushed the PR-3442-DynamoDB-DateTimeEpoch branch from 5eba8fd to 037f1b2 Compare February 27, 2025 18:19
@ashishdhingra ashishdhingra force-pushed the PR-3442-DynamoDB-DateTimeEpoch branch from 037f1b2 to 7bce707 Compare February 27, 2025 18:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants