Skip to content

Commit af5341f

Browse files
committed
chore(core/protocols): dynamodb serde performance adjustments
1 parent c44350d commit af5341f

File tree

24 files changed

+684
-127
lines changed

24 files changed

+684
-127
lines changed

clients/client-dynamodb/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@aws-crypto/sha256-js": "5.2.0",
2525
"@aws-sdk/core": "*",
2626
"@aws-sdk/credential-provider-node": "*",
27+
"@aws-sdk/dynamodb-codec": "*",
2728
"@aws-sdk/middleware-endpoint-discovery": "*",
2829
"@aws-sdk/middleware-host-header": "*",
2930
"@aws-sdk/middleware-logger": "*",

clients/client-dynamodb/src/runtimeConfig.shared.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// smithy-typescript generated code
22
import { AwsSdkSigV4Signer } from "@aws-sdk/core";
33
import { AwsJson1_0Protocol } from "@aws-sdk/core/protocols";
4+
import { DynamoDBJsonCodec } from "@aws-sdk/dynamodb-codec";
45
import { NoOpLogger } from "@smithy/smithy-client";
56
import { IdentityProviderConfig } from "@smithy/types";
67
import { parseUrl } from "@smithy/url-parser";
@@ -37,6 +38,7 @@ export const getRuntimeConfig = (config: DynamoDBClientConfig) => {
3738
defaultNamespace: "com.amazonaws.dynamodb",
3839
serviceTarget: "DynamoDB_20120810",
3940
awsQueryCompatible: false,
41+
jsonCodec: new DynamoDBJsonCodec(),
4042
}),
4143
serviceId: config?.serviceId ?? "DynamoDB",
4244
urlParser: config?.urlParser ?? parseUrl,

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AddProtocolConfig.java

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,18 @@
3636
*/
3737
@SmithyInternalApi
3838
public final class AddProtocolConfig implements TypeScriptIntegration {
39-
4039
static {
4140
init();
4241
}
4342

43+
public static final Map<ShapeId, Consumer<TypeScriptWriter>> CUSTOMIZATIONS = MapUtils.of(
44+
ShapeId.from("com.amazonaws.dynamodb#DynamoDB_20120810"),
45+
writer -> {
46+
writer.addImport("DynamoDBJsonCodec", null, AwsDependency.DYNAMODB_CODEC);
47+
writer.write("jsonCodec: new DynamoDBJsonCodec(),");
48+
}
49+
);
50+
4451
static void init() {
4552
List<ShapeId> allowed = List.of(
4653
AwsJson1_0Trait.ID,
@@ -114,7 +121,8 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
114121
AwsDependency.AWS_SDK_CORE, "/protocols");
115122
writer.write("""
116123
new AwsRestXmlProtocol({
117-
defaultNamespace: $S, xmlNamespace: $S,
124+
defaultNamespace: $S,
125+
xmlNamespace: $S,
118126
})""",
119127
namespace,
120128
xmlns
@@ -132,7 +140,7 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
132140
new AwsQueryProtocol({
133141
defaultNamespace: $S,
134142
xmlNamespace: $S,
135-
version: $S
143+
version: $S,
136144
})""",
137145
namespace,
138146
xmlns,
@@ -151,7 +159,7 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
151159
new AwsEc2QueryProtocol({
152160
defaultNamespace: $S,
153161
xmlNamespace: $S,
154-
version: $S
162+
version: $S,
155163
})""",
156164
namespace,
157165
xmlns,
@@ -174,16 +182,22 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
174182
writer.addImportSubmodule(
175183
"AwsJson1_0Protocol", null,
176184
AwsDependency.AWS_SDK_CORE, "/protocols");
177-
writer.write(
185+
writer.openBlock(
178186
"""
179187
new AwsJson1_0Protocol({
180-
defaultNamespace: $S,
181-
serviceTarget: $S,
182-
awsQueryCompatible: $L
188+
defaultNamespace: $S,
189+
serviceTarget: $S,
190+
awsQueryCompatible: $L,""",
191+
"""
183192
})""",
184193
namespace,
185194
rpcTarget,
186-
awsQueryCompat
195+
awsQueryCompat,
196+
() -> {
197+
if (CUSTOMIZATIONS.containsKey(settings.getService())) {
198+
CUSTOMIZATIONS.get(settings.getService()).accept(writer);
199+
}
200+
}
187201
);
188202
}
189203
);
@@ -193,16 +207,22 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
193207
writer.addImportSubmodule(
194208
"AwsJson1_1Protocol", null,
195209
AwsDependency.AWS_SDK_CORE, "/protocols");
196-
writer.write(
210+
writer.openBlock(
197211
"""
198212
new AwsJson1_1Protocol({
199-
defaultNamespace: $S,
200-
serviceTarget: $S,
201-
awsQueryCompatible: $L
213+
defaultNamespace: $S,
214+
serviceTarget: $S,
215+
awsQueryCompatible: $L,""",
216+
"""
202217
})""",
203218
namespace,
204219
rpcTarget,
205-
awsQueryCompat
220+
awsQueryCompat,
221+
() -> {
222+
if (CUSTOMIZATIONS.containsKey(settings.getService())) {
223+
CUSTOMIZATIONS.get(settings.getService()).accept(writer);
224+
}
225+
}
206226
);
207227
}
208228
);
@@ -215,8 +235,8 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
215235
writer.write(
216236
"""
217237
new AwsSmithyRpcV2CborProtocol({
218-
defaultNamespace: $S,
219-
awsQueryCompatible: $L
238+
defaultNamespace: $S,
239+
awsQueryCompatible: $L,
220240
})""",
221241
namespace,
222242
awsQueryCompat

codegen/smithy-aws-typescript-codegen/src/main/java/software/amazon/smithy/aws/typescript/codegen/AwsDependency.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ public enum AwsDependency implements Dependency {
101101
REGION_CONFIG_RESOLVER(NORMAL_DEPENDENCY, "@aws-sdk/region-config-resolver"),
102102

103103
CLIENT_DYNAMODB_PEER(PEER_DEPENDENCY, "@aws-sdk/client-dynamodb", "^3.0.0"),
104-
UTIL_DYNAMODB(NORMAL_DEPENDENCY, "@aws-sdk/util-dynamodb", "*");
105-
104+
UTIL_DYNAMODB(NORMAL_DEPENDENCY, "@aws-sdk/util-dynamodb", "*"),
105+
DYNAMODB_CODEC(NORMAL_DEPENDENCY, "@aws-sdk/dynamodb-codec");
106106

107107
public final String packageName;
108108
public final String version;

packages/core/src/submodules/protocols/json/AwsJson1_0Protocol.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { AwsJsonRpcProtocol } from "./AwsJsonRpcProtocol";
2+
import type { JsonCodec } from "./JsonCodec";
23

34
/**
45
* @public
@@ -9,15 +10,18 @@ export class AwsJson1_0Protocol extends AwsJsonRpcProtocol {
910
defaultNamespace,
1011
serviceTarget,
1112
awsQueryCompatible,
13+
jsonCodec,
1214
}: {
1315
defaultNamespace: string;
1416
serviceTarget: string;
1517
awsQueryCompatible?: boolean;
18+
jsonCodec?: JsonCodec;
1619
}) {
1720
super({
1821
defaultNamespace,
1922
serviceTarget,
2023
awsQueryCompatible,
24+
jsonCodec,
2125
});
2226
}
2327

packages/core/src/submodules/protocols/json/AwsJson1_1Protocol.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { AwsJsonRpcProtocol } from "./AwsJsonRpcProtocol";
2+
import type { JsonCodec } from "./JsonCodec";
23

34
/**
45
* @public
@@ -9,15 +10,18 @@ export class AwsJson1_1Protocol extends AwsJsonRpcProtocol {
910
defaultNamespace,
1011
serviceTarget,
1112
awsQueryCompatible,
13+
jsonCodec,
1214
}: {
1315
defaultNamespace: string;
1416
serviceTarget: string;
1517
awsQueryCompatible?: boolean;
18+
jsonCodec?: JsonCodec;
1619
}) {
1720
super({
1821
defaultNamespace,
1922
serviceTarget,
2023
awsQueryCompatible,
24+
jsonCodec,
2125
});
2226
}
2327

packages/core/src/submodules/protocols/json/AwsJsonRpcProtocol.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,26 @@ export abstract class AwsJsonRpcProtocol extends RpcProtocol {
3232
defaultNamespace,
3333
serviceTarget,
3434
awsQueryCompatible,
35+
jsonCodec,
3536
}: {
3637
defaultNamespace: string;
3738
serviceTarget: string;
3839
awsQueryCompatible?: boolean;
40+
jsonCodec?: JsonCodec;
3941
}) {
4042
super({
4143
defaultNamespace,
4244
});
4345
this.serviceTarget = serviceTarget;
44-
this.codec = new JsonCodec({
45-
timestampFormat: {
46-
useTrait: true,
47-
default: 7 as const satisfies TimestampEpochSecondsSchema,
48-
},
49-
jsonName: false,
50-
});
46+
this.codec =
47+
jsonCodec ??
48+
new JsonCodec({
49+
timestampFormat: {
50+
useTrait: true,
51+
default: 7 as const satisfies TimestampEpochSecondsSchema,
52+
},
53+
jsonName: false,
54+
});
5155
this.serializer = this.codec.createSerializer();
5256
this.deserializer = this.codec.createDeserializer();
5357
this.awsQueryCompatible = !!awsQueryCompatible;

packages/core/src/submodules/protocols/json/JsonShapeDeserializer.ts

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -42,49 +42,51 @@ export class JsonShapeDeserializer extends SerdeContextConfig implements ShapeDe
4242
return this._read(schema, data);
4343
}
4444

45-
private _read(schema: Schema, value: unknown): any {
45+
protected _read(schema: Schema, value: unknown): any {
4646
const isObject = value !== null && typeof value === "object";
4747

4848
const ns = NormalizedSchema.of(schema);
4949

50-
// === aggregate types ===
51-
if (ns.isListSchema() && Array.isArray(value)) {
52-
const listMember = ns.getValueSchema();
53-
const out = [] as any[];
54-
const sparse = !!ns.getMergedTraits().sparse;
55-
for (const item of value) {
56-
if (sparse || item != null) {
57-
out.push(this._read(listMember, item));
50+
if (isObject) {
51+
if (ns.isStructSchema()) {
52+
const out = {} as any;
53+
for (const [memberName, memberSchema] of deserializingStructIterator(
54+
ns,
55+
value,
56+
this.settings.jsonName ? "jsonName" : false
57+
)) {
58+
const fromKey = this.settings.jsonName ? memberSchema.getMergedTraits().jsonName ?? memberName : memberName;
59+
const deserializedValue = this._read(memberSchema, (value as any)[fromKey]);
60+
if (deserializedValue != null) {
61+
out[memberName] = deserializedValue;
62+
}
5863
}
64+
return out;
5965
}
60-
return out;
61-
} else if (ns.isMapSchema() && isObject) {
62-
const mapMember = ns.getValueSchema();
63-
const out = {} as any;
64-
const sparse = !!ns.getMergedTraits().sparse;
65-
for (const [_k, _v] of Object.entries(value)) {
66-
if (sparse || _v != null) {
67-
out[_k] = this._read(mapMember, _v);
66+
if (Array.isArray(value) && ns.isListSchema()) {
67+
const listMember = ns.getValueSchema();
68+
const out = [] as any[];
69+
const sparse = !!ns.getMergedTraits().sparse;
70+
for (const item of value) {
71+
if (sparse || item != null) {
72+
out.push(this._read(listMember, item));
73+
}
6874
}
75+
return out;
6976
}
70-
return out;
71-
} else if (ns.isStructSchema() && isObject) {
72-
const out = {} as any;
73-
for (const [memberName, memberSchema] of deserializingStructIterator(
74-
ns,
75-
value,
76-
this.settings.jsonName ? "jsonName" : false
77-
)) {
78-
const fromKey = this.settings.jsonName ? memberSchema.getMergedTraits().jsonName ?? memberName : memberName;
79-
const deserializedValue = this._read(memberSchema, (value as any)[fromKey]);
80-
if (deserializedValue != null) {
81-
out[memberName] = deserializedValue;
77+
if (ns.isMapSchema()) {
78+
const mapMember = ns.getValueSchema();
79+
const out = {} as any;
80+
const sparse = !!ns.getMergedTraits().sparse;
81+
for (const [_k, _v] of Object.entries(value)) {
82+
if (sparse || _v != null) {
83+
out[_k] = this._read(mapMember, _v);
84+
}
8285
}
86+
return out;
8387
}
84-
return out;
8588
}
8689

87-
// === simple types ===
8890
if (ns.isBlobSchema() && typeof value === "string") {
8991
return fromBase64(value);
9092
}
@@ -95,6 +97,7 @@ export class JsonShapeDeserializer extends SerdeContextConfig implements ShapeDe
9597
if (isJson) {
9698
return LazyJsonString.from(value);
9799
}
100+
return value;
98101
}
99102

100103
if (ns.isTimestampSchema() && value != null) {
@@ -136,6 +139,7 @@ export class JsonShapeDeserializer extends SerdeContextConfig implements ShapeDe
136139
case "NaN":
137140
return NaN;
138141
}
142+
return value;
139143
}
140144

141145
if (ns.isDocumentSchema()) {
@@ -154,7 +158,7 @@ export class JsonShapeDeserializer extends SerdeContextConfig implements ShapeDe
154158
}
155159
}
156160

157-
// covers string, numeric, boolean, document, bigDecimal
161+
// covers boolean, bigint (long/BigInt), bigDecimal
158162
return value;
159163
}
160164
}

0 commit comments

Comments
 (0)