-
Notifications
You must be signed in to change notification settings - Fork 910
DynamoDb Enhanced perf testing #1726
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -134,6 +134,17 @@ | |
<artifactId>netty-nio-client</artifactId> | ||
<version>${awsjavasdk.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>software.amazon.awssdk</groupId> | ||
<artifactId>dynamodb-enhanced</artifactId> | ||
<!-- TODO: Remove '-PREVIEW' when the client is GA --> | ||
<version>${awsjavasdk.version}-PREVIEW</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.amazonaws</groupId> | ||
<artifactId>aws-java-sdk-dynamodb</artifactId> | ||
<version>1.11.748</version> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need v1 here long term? Once we have a good baseline for v2, can we just track v2? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can remove it in the future, but while we're iterating on the performance we should have this in here. |
||
</dependency> | ||
<dependency> | ||
<groupId>io.netty</groupId> | ||
<artifactId>netty-tcnative-boringssl-static</artifactId> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,9 @@ | |
import software.amazon.awssdk.benchmark.apicall.protocol.XmlProtocolBenchmark; | ||
import software.amazon.awssdk.benchmark.coldstart.V2DefaultClientCreationBenchmark; | ||
import software.amazon.awssdk.benchmark.coldstart.V2OptimizedClientCreationBenchmark; | ||
import software.amazon.awssdk.benchmark.enhanced.dynamodb.EnhancedClientGetOverheadBenchmark; | ||
import software.amazon.awssdk.benchmark.enhanced.dynamodb.EnhancedClientPutOverheadBenchmark; | ||
import software.amazon.awssdk.benchmark.enhanced.dynamodb.V1MapperComparisonBenchmark; | ||
import software.amazon.awssdk.utils.Logger; | ||
|
||
|
||
|
@@ -58,6 +61,12 @@ public class BenchmarkRunner { | |
V2OptimizedClientCreationBenchmark.class.getSimpleName(), | ||
V2DefaultClientCreationBenchmark.class.getSimpleName()); | ||
|
||
private static final List<String> MAPPER_BENCHMARKS = Arrays.asList( | ||
EnhancedClientGetOverheadBenchmark.class.getSimpleName(), | ||
EnhancedClientPutOverheadBenchmark.class.getSimpleName(), | ||
V1MapperComparisonBenchmark.class.getSimpleName() | ||
); | ||
|
||
private static final Logger log = Logger.loggerFor(BenchmarkRunner.class); | ||
|
||
private final List<String> benchmarksToRun; | ||
|
@@ -74,6 +83,7 @@ public static void main(String... args) throws RunnerException, JsonProcessingEx | |
benchmarksToRun.addAll(ASYNC_BENCHMARKS); | ||
benchmarksToRun.addAll(PROTOCOL_BENCHMARKS); | ||
benchmarksToRun.addAll(COLD_START_BENCHMARKS); | ||
benchmarksToRun.addAll(MAPPER_BENCHMARKS); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we add ddb enhanced client data to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See previous comment for getting baseline |
||
|
||
BenchmarkRunner runner = new BenchmarkRunner(benchmarksToRun); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
package software.amazon.awssdk.benchmark.enhanced.dynamodb; | ||
|
||
import static software.amazon.awssdk.core.client.config.SdkClientOption.ENDPOINT; | ||
|
||
import java.io.IOException; | ||
import java.io.UncheckedIOException; | ||
import java.net.URI; | ||
import java.util.Map; | ||
import org.openjdk.jmh.annotations.Benchmark; | ||
import org.openjdk.jmh.annotations.BenchmarkMode; | ||
import org.openjdk.jmh.annotations.Fork; | ||
import org.openjdk.jmh.annotations.Measurement; | ||
import org.openjdk.jmh.annotations.Mode; | ||
import org.openjdk.jmh.annotations.Param; | ||
import org.openjdk.jmh.annotations.Scope; | ||
import org.openjdk.jmh.annotations.Setup; | ||
import org.openjdk.jmh.annotations.State; | ||
import org.openjdk.jmh.annotations.Warmup; | ||
import org.openjdk.jmh.infra.Blackhole; | ||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; | ||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; | ||
import software.amazon.awssdk.benchmark.utils.MockHttpClient; | ||
import software.amazon.awssdk.core.client.config.SdkClientConfiguration; | ||
import software.amazon.awssdk.core.interceptor.Context; | ||
import software.amazon.awssdk.core.interceptor.ExecutionAttributes; | ||
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; | ||
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; | ||
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; | ||
import software.amazon.awssdk.enhanced.dynamodb.Key; | ||
import software.amazon.awssdk.enhanced.dynamodb.TableSchema; | ||
import software.amazon.awssdk.protocols.json.AwsJsonProtocol; | ||
import software.amazon.awssdk.protocols.json.AwsJsonProtocolFactory; | ||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient; | ||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue; | ||
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException; | ||
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; | ||
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; | ||
import software.amazon.awssdk.services.dynamodb.transform.PutItemRequestMarshaller; | ||
import software.amazon.awssdk.utils.IoUtils; | ||
|
||
@BenchmarkMode(Mode.Throughput) | ||
@Warmup(iterations = 5) | ||
@Measurement(iterations = 5) | ||
@Fork(2) | ||
@State(Scope.Benchmark) | ||
public class EnhancedClientGetOverheadBenchmark { | ||
private static final AwsJsonProtocolFactory JSON_PROTOCOL_FACTORY = AwsJsonProtocolFactory | ||
.builder() | ||
.clientConfiguration(SdkClientConfiguration.builder() | ||
.option(ENDPOINT, URI.create("https://dynamodb.amazonaws.com")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's this endpoint? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was to stop the numerous profile loader related log messages |
||
.build()) | ||
.defaultServiceExceptionSupplier(DynamoDbException::builder) | ||
.protocol(AwsJsonProtocol.AWS_JSON) | ||
.protocolVersion("1.0") | ||
.build(); | ||
|
||
private static final PutItemRequestMarshaller PUT_ITEM_REQUEST_MARSHALLER = | ||
new PutItemRequestMarshaller(JSON_PROTOCOL_FACTORY); | ||
|
||
private static final V2ItemFactory ITEM_FACTORY = new V2ItemFactory(); | ||
|
||
private final Key testKey = Key.builder().partitionValue("key").build(); | ||
|
||
|
||
@Benchmark | ||
public Object lowLevelGet(TestState s) { | ||
return s.dynamoDb.getItem(GetItemRequest.builder().build()); | ||
} | ||
|
||
@Benchmark | ||
public Object enhanceGet(TestState s) { | ||
return s.table.getItem(testKey); | ||
} | ||
|
||
@State(Scope.Benchmark) | ||
public static class TestState { | ||
private DynamoDbClient dynamoDb; | ||
|
||
@Param({"TINY", "SMALL", "HUGE", "HUGE_FLAT"}) | ||
private TestItem testItem; | ||
|
||
private DynamoDbTable table; | ||
|
||
@Setup | ||
public void setup(Blackhole bh) { | ||
dynamoDb = DynamoDbClient.builder() | ||
.credentialsProvider(StaticCredentialsProvider.create( | ||
AwsBasicCredentials.create("akid", "skid"))) | ||
.httpClient(new MockHttpClient(testItem.responseContent, "{}")) | ||
.overrideConfiguration(o -> o.addExecutionInterceptor(new ExecutionInterceptor() { | ||
@Override | ||
public void afterUnmarshalling(Context.AfterUnmarshalling context, | ||
ExecutionAttributes executionAttributes) { | ||
bh.consume(context); | ||
bh.consume(executionAttributes); | ||
} | ||
})) | ||
.build(); | ||
|
||
DynamoDbEnhancedClient ddbEnh = DynamoDbEnhancedClient.builder() | ||
.dynamoDbClient(dynamoDb) | ||
.build(); | ||
|
||
table = ddbEnh.table(testItem.name(), testItem.tableSchema); | ||
} | ||
} | ||
|
||
public enum TestItem { | ||
TINY(marshall(ITEM_FACTORY.tiny()), V2ItemFactory.TINY_BEAN_TABLE_SCHEMA), | ||
SMALL(marshall(ITEM_FACTORY.small()), V2ItemFactory.SMALL_BEAN_TABLE_SCHEMA), | ||
HUGE(marshall(ITEM_FACTORY.huge()), V2ItemFactory.HUGE_BEAN_TABLE_SCHEMA), | ||
HUGE_FLAT(marshall(ITEM_FACTORY.hugeFlat()), V2ItemFactory.HUGE_BEAN_FLAT_TABLE_SCHEMA) | ||
; | ||
|
||
private String responseContent; | ||
private TableSchema tableSchema; | ||
|
||
TestItem(String responseContent, TableSchema tableSchema) { | ||
this.responseContent = responseContent; | ||
this.tableSchema = tableSchema; | ||
} | ||
} | ||
|
||
private static String marshall(Map<String, AttributeValue> item) { | ||
return PUT_ITEM_REQUEST_MARSHALLER.marshall(PutItemRequest.builder().item(item).build()) | ||
.contentStreamProvider().map(cs -> { | ||
try { | ||
return IoUtils.toUtf8String(cs.newStream()); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
}).orElse(null); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
package software.amazon.awssdk.benchmark.enhanced.dynamodb; | ||
|
||
import java.util.Map; | ||
import org.openjdk.jmh.annotations.Benchmark; | ||
import org.openjdk.jmh.annotations.BenchmarkMode; | ||
import org.openjdk.jmh.annotations.Fork; | ||
import org.openjdk.jmh.annotations.Measurement; | ||
import org.openjdk.jmh.annotations.Mode; | ||
import org.openjdk.jmh.annotations.Param; | ||
import org.openjdk.jmh.annotations.Scope; | ||
import org.openjdk.jmh.annotations.Setup; | ||
import org.openjdk.jmh.annotations.State; | ||
import org.openjdk.jmh.annotations.Warmup; | ||
import org.openjdk.jmh.infra.Blackhole; | ||
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; | ||
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; | ||
import software.amazon.awssdk.benchmark.utils.MockHttpClient; | ||
import software.amazon.awssdk.core.interceptor.Context; | ||
import software.amazon.awssdk.core.interceptor.ExecutionAttributes; | ||
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; | ||
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; | ||
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; | ||
import software.amazon.awssdk.enhanced.dynamodb.TableSchema; | ||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient; | ||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue; | ||
|
||
@BenchmarkMode(Mode.Throughput) | ||
@Warmup(iterations = 5) | ||
@Measurement(iterations = 5) | ||
@Fork(2) | ||
@State(Scope.Benchmark) | ||
public class EnhancedClientPutOverheadBenchmark { | ||
@Benchmark | ||
public void lowLevelPut(TestState s) { | ||
s.ddb.putItem(r -> r.item(s.testItem.av)); | ||
} | ||
|
||
@Benchmark | ||
public void enhancedPut(TestState s) { | ||
s.enhTable.putItem(s.testItem.bean); | ||
} | ||
|
||
@State(Scope.Benchmark) | ||
public static class TestState { | ||
@Param({"TINY", "SMALL", "HUGE", "HUGE_FLAT"}) | ||
private TestItem testItem; | ||
private DynamoDbClient ddb; | ||
|
||
private DynamoDbTable enhTable; | ||
|
||
@Setup | ||
public void setup(Blackhole bh) { | ||
ddb = DynamoDbClient.builder() | ||
.credentialsProvider(StaticCredentialsProvider.create( | ||
AwsBasicCredentials.create("akid", "skid"))) | ||
.httpClient(new MockHttpClient("{}", "{}")) | ||
.overrideConfiguration(c -> c.addExecutionInterceptor(new ExecutionInterceptor() { | ||
@Override | ||
public void afterUnmarshalling(Context.AfterUnmarshalling context, | ||
ExecutionAttributes executionAttributes) { | ||
bh.consume(context); | ||
bh.consume(executionAttributes); | ||
} | ||
})) | ||
.build(); | ||
|
||
DynamoDbEnhancedClient ddbEnh = DynamoDbEnhancedClient.builder() | ||
.dynamoDbClient(ddb) | ||
.build(); | ||
|
||
enhTable = ddbEnh.table(testItem.name(), testItem.tableSchema); | ||
} | ||
} | ||
|
||
public enum TestItem { | ||
TINY, | ||
SMALL, | ||
HUGE, | ||
HUGE_FLAT | ||
; | ||
|
||
private static final V2ItemFactory FACTORY = new V2ItemFactory(); | ||
|
||
private Map<String, AttributeValue> av; | ||
|
||
private TableSchema tableSchema; | ||
private Object bean; | ||
|
||
static { | ||
TINY.av = FACTORY.tiny(); | ||
TINY.tableSchema = V2ItemFactory.TINY_BEAN_TABLE_SCHEMA; | ||
TINY.bean = FACTORY.tinyBean(); | ||
|
||
SMALL.av = FACTORY.small(); | ||
SMALL.tableSchema = V2ItemFactory.SMALL_BEAN_TABLE_SCHEMA; | ||
SMALL.bean = FACTORY.smallBean(); | ||
|
||
HUGE.av = FACTORY.huge(); | ||
HUGE.tableSchema = V2ItemFactory.HUGE_BEAN_TABLE_SCHEMA; | ||
HUGE.bean = FACTORY.hugeBean(); | ||
|
||
HUGE_FLAT.av = FACTORY.hugeFlat(); | ||
HUGE_FLAT.tableSchema = V2ItemFactory.HUGE_BEAN_FLAT_TABLE_SCHEMA; | ||
HUGE_FLAT.bean = FACTORY.hugeBeanFlat(); | ||
} | ||
} | ||
} |
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.
Add a TODO here so we don't forget?
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.
Sure