-
Notifications
You must be signed in to change notification settings - Fork 2k
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
AWS DynamoDB Membership Provider #2008
Conversation
/// <summary> | ||
/// AWS DynamoDB basic Storage provider | ||
/// </summary> | ||
public class DynamoDBStorage : IStorageProvider |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why storage provider is part of membership table pull request? I thought you had a separate pr for it.
@galvesribeiro , my recommendation (based on #2005 (comment) ) is to reopen this PR, remove all the code unrelated to MBR provider (remove storage provider) and I will review it thoroughly. |
Ok, this PR has the final membership and statistics code. All the changes on it are on This PR still need be rebased on master once #2007 is merged since it depends on a common class ( |
@gabikliot rebased on latest master which has the Storage Provider already merged. Can you review it please? Btw, kudos to @shayhatsor for the tips on the membership protocol :) |
Please separate and remove the statistics/metrics from this PR to a sepaarte PR. |
What is the problem with that? The PR was intent to be with it. It is 3 no-brain classes... |
toDelete.Add(record.GetKeys()); | ||
} | ||
|
||
if (records.Count <= 25) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need a special case here, the general loop below is good. Also, 25? at least lets use constant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DynamoDB has a MAX_BATCH_SIZE of 25. Ok, createt a constant and removed the IF.
It is no brainier for you. For me, as a reviewer that takes responsibility on what he reviews, that makes MY life much harder. Specifically, I want to review the MBR now with 100% attention. The extra unrelated code confuses me. |
In your AWS Storage Put method: What happens if you call this method without conditionExpression and without conditionValues - that is: you THINK there is no previous version of the row, but when the call arrives to AWS the row is there. Would the call overwrite the prev data or would it fail? |
I think you have to go back and add detailed explanation on the AWS storage class about concurrency semantics of each operation. |
} | ||
|
||
if (result == false) | ||
logger.Warn(ErrorCode.MembershipBase, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this logging be moved inside the preceding catch clause?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
try | ||
{ | ||
var conditionalValues = new Dictionary<string, AttributeValue> { { CURRENT_ETAG_ALIAS, new AttributeValue { N = etag } } }; | ||
var expression = $"{SiloInstanceRecord.ETAG_PROPERTY_NAME} = {CURRENT_ETAG_ALIAS}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This generic expression
could have a more descriptive name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Ok... All the comments addressed. @veikkoeeva thanks for get back on the statistics but, I'm removing that from this PR as @gabikliot requested and will put another one with those and I appreciate if you can have some time to review that. I noticed that probably there is a lack of knowledge of how DynamoDB works which is ok and I should had it detailed on the PR before submit it so let me explain a bit here... DynamoDB has only 3 Write operations:
All of them doesn't deal with ETag natively (unlike in Azure Table Storage or auto-increment fields in SQL which has native ways to deal with it) and any kind of conditional operation isn't implicitly implied. So we have to deal with it by using Conditional Expressions that you can see on those string around the code. Basically, if you want to PUT something and make sure there is no other row with the same ID (as we do while Creating a new record) we need to use an expression like that: var expression = $"attribute_not_exists({SiloInstanceRecord.DEPLOYMENT_ID_PROPERTY_NAME}) AND attribute_not_exists({SiloInstanceRecord.SILO_IDENTITY_PROPERTY_NAME})";
await storage.PutEntryAsync(TABLE_NAME_DEFAULT_VALUE, tableEntry.GetFields(true), expression); So if there is a row with the same keys as the ones in the expression, it will throw Otherwise, if you want to replace it (blind write), just call Put without any conditional expression. The same happens to the case where we need to check the ETag. var etagConditionalExpression = $"{SiloInstanceRecord.ETAG_PROPERTY_NAME} = {CURRENT_ETAG_ALIAS}";
await storage.UpsertEntryAsync(TABLE_NAME_DEFAULT_VALUE, siloEntry.GetKeys(),
siloEntry.GetFields(), etagConditionalExpression, conditionalValues); Expressions are optional and that is the way AWS works. I understand you concern about the dangerous of the blind write. But, think that "just because USA allow people to carry guns, it doesn't mean they should shoot each other". The same should apply here. All the classes are internal, and the provider is only accessible by Orleans Runtime and never directly by the users. The IMHO blind write is not ALWAYS wrong. It is a matter of decision. If you need a condition to be met? Just express it. Remember that SQL can delete all the records of a single table if you don't specify a Please let me know if you need anything else to change or more clarification on how DynamoDB works. |
@galvesribeiro Sure, no problem for the other PR. :) About my comments, it would help to have more comments for people who'd like to see how things are implemented. Then variables named according to their specific purpose and why some default numbers have been chosen maybe with just some appropriately named constant. However, this again is only how I feel. |
@veikkoeeva sure! I addressed all the comments you made. :) |
try | ||
{ | ||
var conditionalValues = new Dictionary<string, AttributeValue> { { CURRENT_ETAG_ALIAS, new AttributeValue { N = etag } } }; | ||
var etagConditionalExpression = $"{SiloInstanceRecord.ETAG_PROPERTY_NAME} = {CURRENT_ETAG_ALIAS}"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should not conditional expression here include also, in adddition to etag, $"attribute_exists({SiloInstanceRecord.DEPLOYMENT_ID_PROPERTY_NAME}) AND attribute_exists({SiloInstanceRecord.SILO_IDENTITY_PROPERTY_NAME})";
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change that to be more explicit but it isn't required. A row is never saved with ETAG as null, the initial value is 0, so basically this would remove the need for the double check on the key. But I agree with you that it would make more clear for the reader the intentions with the expression even if it is not required by the DynamoDB expression in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rethinking on that, no... I think we shouldn't change the filter and leave only the etag. Since the ETag is never null (initial value is 0), and the row will never be inserted without DEPLOYMENT_ID and SILO_IDENTITY since they are the keys, there is no point in a row to has ETag but no keys :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless there is a bug?
@galvesribeiro , thanks for the explanation! It makes much more sense now. Overall, looks good now, except for a couple of comments I made. |
{ | ||
var membershipEntry = CreateMembershipEntryForTest(); | ||
|
||
var data = await membershipTable.ReadAll(); | ||
Assert.NotNull(data); | ||
Assert.Equal(0, data.Members.Count); | ||
|
||
bool ok = await membershipTable.InsertRow(membershipEntry, data.Version.Next()); | ||
TableVersion nextTableVersion = data.Version.Next(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this line was added by mistake. Please run the tests on Azure to make sure you haven't mistakenly broke anything related to the extended protocol.
@galvesribeiro, as most of this work was done while consulting me - it looks good to me 😄 |
Ok guys, thanks for the feedback @shayhatsor and @gabikliot. I made small changes on the tests to make sure we get more Asserts as you guys pointed and also run the Azure tests to make sure nothing were affected and as expected (the default for |
Just for the sake of notes here, I'll be adding that explanation in a final PR on all the AWS lib which will make some cosmetic changes like error enum, messages, and comments. Rest assured of it. |
@galvesribeiro, thanks for the hard work and dedication ! |
Thanks to everyone for the feedback! 😄 |
Implementation of #2006 which is part of #2005