-
Notifications
You must be signed in to change notification settings - Fork 467
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
Conversation
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) |
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 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.
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.
ya, kept this and marked as deprecated.
import software.amazon.kinesis.leases.dynamodb.TableCreatorCallback; | ||
|
||
@Slf4j | ||
public class LeaseIntegrationBillingModePayPerRequestTest extends LeaseIntegrationTest { |
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.
Lets keep the existing integ tests
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.
since keeping the old function signature, will keep the integ tests
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.
Left one comment to not remove public interface method
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(); |
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.
Let's not change the old code path - this will change the behavior for any customer classes that use this.
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.
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(); |
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.
See earlier comment
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.
How did we test this? Can we add unit tests? |
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(). |
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.
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
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.
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); |
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.
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.
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.
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. |
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.
Leaving this up to your preference, but I personally don't think this comment is needed since it's mentioned the interface's javadoc.
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.
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())) |
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 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).
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.
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); |
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 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()
.
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.
yes, updated this as part of removing the any()
where ever possible
if (tableStatus() != null) { | ||
return newTableCreated; | ||
} | ||
} catch (DependencyException de) { |
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 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.
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 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); |
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.
Same with earlier comment
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.
updated
when(mockCreateTableFuture.get(anyLong(), any())).thenReturn(null); | ||
|
||
final boolean result = leaseRefresher.createLeaseTableIfNotExists(10L, 10L); | ||
|
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.
Can we verify interactions w/ the ddb client?
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.
Added, also for other dependencies.
|
||
Assert.assertTrue(result); | ||
} | ||
|
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.
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
- 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
- If we throw a
ResourceNotFoundException
, we should expect to return true. - If we throw a
LimitExceededException
, we should propagate up aProvisionedThroughputException
. - If we throw a
DynamoDBException
we should throw aDependencyException
. - If we throw a
TimeoutException
we should throw aDependencyException
.
As with the earlier comment, we should also be verifying interactions between dependencies.
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.
For DescribeTable, I have kept the existing behavior and added a tests for all above.
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.
Left a few comments on the unit tests
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.
LGTM. Thanks for the PR!
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.