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

Create DynamoDB tables on On-Demand billing mode by default. #854

Merged
merged 4 commits into from
Oct 6, 2021
Merged

Create DynamoDB tables on On-Demand billing mode by default. #854

merged 4 commits into from
Oct 6, 2021

Conversation

shanmsac
Copy link
Contributor

  • This will enable KCL to create the DynamoDB tables - Lease and ShardProgress with the On-Demand billing mode instead of provisioned billing mode.
  • Updated the LeaseRefresher interface function createLeaseTableIfNotExists() to remove the provision mode parameters.
  • Removed the integration test "DynamoDBLeaseRenewerBillingModePayPerRequestIntegrationTest.java" as PayPerRequest mode is tested by default integration test.

Signed-off-by: Sachin Sundar P S shanmsac@amazon.com

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

This will enable KCL to create the DynamoDB tables - Lease and
ShardProgress with the On-Demand billing mode instead of provisioned
billing mode.
* @return true if we created a new table (table didn't exist before)
*
* @throws ProvisionedThroughputException if we cannot create the lease table due to per-AWS-account capacity
* restrictions.
* @throws DependencyException if DynamoDB createTable fails in an unexpected way
*/
boolean createLeaseTableIfNotExists(Long readCapacity, Long writeCapacity)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a public interface customers might be using; let's not mess with these signatures. We can make createLeaseTableIfNotExists() a separate signature to preserve backwards-compatible behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ya, kept this and marked as deprecated.

import software.amazon.kinesis.leases.dynamodb.TableCreatorCallback;

@Slf4j
public class LeaseIntegrationBillingModePayPerRequestTest extends LeaseIntegrationTest {
Copy link
Contributor

Choose a reason for hiding this comment

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

Lets keep the existing integ tests

Copy link
Contributor Author

Choose a reason for hiding this comment

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

since keeping the old function signature, will keep the integ tests

Copy link
Contributor

@joshua-kim joshua-kim left a comment

Choose a reason for hiding this comment

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

Left one comment to not remove public interface method

@shanmsac
Copy link
Contributor Author

Updated as per comments

@@ -162,6 +161,16 @@ public DynamoDBLeaseRefresher(final String table, final DynamoDbAsyncClient dyna
@Override
public boolean createLeaseTableIfNotExists(@NonNull final Long readCapacity, @NonNull final Long writeCapacity)
throws ProvisionedThroughputException, DependencyException {
// DynamoDB is now created in PayPerRequest billing mode by default. Keeping this for backward compatibility.
return createLeaseTableIfNotExists();
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's not change the old code path - this will change the behavior for any customer classes that use this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

addressed this.

@@ -110,11 +110,17 @@ private void throwExceptions(String methodName, ExceptionThrowingLeaseRefresherM

@Override
public boolean createLeaseTableIfNotExists(Long readCapacity, Long writeCapacity)
throws ProvisionedThroughputException, DependencyException {
return createLeaseTableIfNotExists();
Copy link
Contributor

Choose a reason for hiding this comment

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

See earlier comment

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.

@joshua-kim
Copy link
Contributor

How did we test this? Can we add unit tests?

@shanmsac
Copy link
Contributor Author

added unit tests for both table creation methods

@@ -29,17 +29,31 @@

/**
* Creates the table that will store leases. Succeeds if table already exists.
*
* Deprecated. Use createLeaseTableIfNotExists().
Copy link
Contributor

Choose a reason for hiding this comment

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

You can use @link or javadoc annotation here to point people over to the new method, it makes it easier to find the correct method since it indexes in ides + links in documentation generators

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to @link.

@@ -211,10 +211,9 @@ public void run() {
@Override
public void initialize() throws ProvisionedThroughputException, DependencyException, IllegalStateException {
final boolean newTableCreated =
leaseRefresher.createLeaseTableIfNotExists(initialLeaseTableReadCapacity, initialLeaseTableWriteCapacity);
Copy link
Contributor

@joshua-kim joshua-kim Oct 1, 2021

Choose a reason for hiding this comment

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

There should be a unit test in DynamoDBLeaseCoordinatorTest to make sure that LeaseRefresher#createLeaseIfNotExists() gets called in DynamoDBLeaseCoordinator#initialize() as part of validating this change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is no DynamoDBLeaseCoordinatorTest or any unit test for this class. Added unit test for initialize() to test this.

//
log.error("Failed to get table status for {}", table, de);
}
// DynamoDB is now created in PayPerRequest billing mode by default. Keeping this for backward compatibility.
Copy link
Contributor

Choose a reason for hiding this comment

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

Leaving this up to your preference, but I personally don't think this comment is needed since it's mentioned the interface's javadoc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ya, updated interface's javadoc to include this.

tableCreatorCallback, LeaseManagementConfig.DEFAULT_REQUEST_TIMEOUT, BillingMode.PROVISIONED);

when(dynamoDbClient.describeTable(any(DescribeTableRequest.class))).thenReturn(mockDescribeTableFuture);
when(mockDescribeTableFuture.get(anyLong(), any()))
Copy link
Contributor

@joshua-kim joshua-kim Oct 1, 2021

Choose a reason for hiding this comment

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

Why any() here? Can we use the specific class we're expecting (if it's not possible to use the exact value, applicable in other spots in this PR as well).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, these came from the previous tests. Updated to use actual class objects.

leaseRefresher = new DynamoDBLeaseRefresher(TABLE_NAME, dynamoDbClient, leaseSerializer, CONSISTENT_READS,
tableCreatorCallback, LeaseManagementConfig.DEFAULT_REQUEST_TIMEOUT, BillingMode.PROVISIONED);

when(dynamoDbClient.describeTable(any(DescribeTableRequest.class))).thenReturn(mockDescribeTableFuture);
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need to use any(DescribeTableRequest.class) since the table name is constant across tests, we can mock against a concrete object like DescribeTableRequest.builder().tableName(TABLE_NAME).build().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, updated this as part of removing the any() where ever possible

if (tableStatus() != null) {
return newTableCreated;
}
} catch (DependencyException de) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this is the existing behavior but I'm wondering why we don't throw this exception here. Seems like it makes more sense to fail here if we're not able to pull the lease table's status and let the caller decide how to proceed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wondered the same while rewriting this function why the exception was consumed. I think it was done as CreateTable call will fail with ResourceInUseException and return false(table already exists) or create the table, so finally its the same behavior for caller. So, I'm keeping this behavior for now.

when(mockDescribeTableFuture.get(anyLong(), any()))
.thenThrow(ResourceNotFoundException.builder().message("Table doesn't exist").build());

when(dynamoDbClient.createTable(any(CreateTableRequest.class))).thenReturn(mockCreateTableFuture);
Copy link
Contributor

Choose a reason for hiding this comment

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

Same with earlier comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

when(mockCreateTableFuture.get(anyLong(), any())).thenReturn(null);

final boolean result = leaseRefresher.createLeaseTableIfNotExists(10L, 10L);

Copy link
Contributor

@joshua-kim joshua-kim Oct 1, 2021

Choose a reason for hiding this comment

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

Can we verify interactions w/ the ddb client?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added, also for other dependencies.


Assert.assertTrue(result);
}

Copy link
Contributor

@joshua-kim joshua-kim Oct 1, 2021

Choose a reason for hiding this comment

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

We've only tested the cases where the table doesn't exist (i.e ddbClient.describeTable() throws a ResourceNotFoundException. Let's add a few other edge cases which can happen (I noticed a few exceptions are thrown in the bootstrap path):

DescribeTable

  1. If we throw a DependencyException, we should either continue trying to create the table (or throw the exception if we decide to propagate it up as per my earlier comment. I don't have a strong opinion on this since it's existing behavior, so feel free to take either approach).

CreateTable

  1. If we throw a ResourceNotFoundException, we should expect to return true.
  2. If we throw a LimitExceededException, we should propagate up a ProvisionedThroughputException.
  3. If we throw a DynamoDBException we should throw a DependencyException.
  4. If we throw a TimeoutException we should throw a DependencyException.

As with the earlier comment, we should also be verifying interactions between dependencies.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For DescribeTable, I have kept the existing behavior and added a tests for all above.

Copy link
Contributor

@joshua-kim joshua-kim left a comment

Choose a reason for hiding this comment

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

Left a few comments on the unit tests

Copy link
Contributor

@joshua-kim joshua-kim left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for the PR!

@joshua-kim joshua-kim merged commit e81969a into awslabs:master Oct 6, 2021
@shanmsac shanmsac deleted the ddb-on-demand branch October 6, 2021 23:06
@shanmsac shanmsac added this to the v2.3.7 milestone Oct 11, 2021
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.

None yet

2 participants