Skip to content

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

Merged
merged 4 commits into from
Mar 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions test/sdk-benchmarks/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure

</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-dynamodb</artifactId>
<version>1.11.748</version>
Copy link
Contributor

Choose a reason for hiding this comment

The 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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;


Expand All @@ -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;
Expand All @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add ddb enhanced client data to baseline.json for regression detection?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See previous comment for getting baseline


BenchmarkRunner runner = new BenchmarkRunner(benchmarksToRun);

Expand Down
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"))
Copy link
Contributor

Choose a reason for hiding this comment

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

What's this endpoint?

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 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();
}
}
}
Loading