Skip to content

Commit 7454d40

Browse files
committed
perf(endpoints): Reduce allocation in endpoint resolution
Add optimized Endpoint factory methods and a ThreadLocal partition cache to reduce per-resolution allocation overhead: 1. Endpoint.ofAttribute(url, key, value) — uses singletonMap instead of HashMap(16) for the common single-attribute case (AUTH_SCHEMES) 2. Endpoint.of(url) — zero-attribute endpoint without builder 3. Endpoint.ofAttributes(url, k1, v1, k2, v2) — two-attribute case Codegen changes: - CodeGeneratorVisitor emits Endpoint.ofAttribute/of instead of builder pattern when attribute count is known at compile time - BddResultCodeGeneratorVisitor emits same factories for BDD path Runtime changes (RulesFunctions.java.resource): - ThreadLocal awsPartition(region) cache retains last region/partition mapping (~100% hit rate in steady-state single-region usage) - Removed uriEncode and isVirtualHostableS3Bucket caches (profiling showed cache-check overhead comparable to recomputation)
1 parent e745bc1 commit 7454d40

8 files changed

Lines changed: 202 additions & 37 deletions

File tree

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/CodeGeneratorVisitor.java

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -328,15 +328,64 @@ private String callParams(String ruleId) {
328328

329329
@Override
330330
public Void visitEndpointExpression(EndpointExpression e) {
331-
builder.add("return $T.builder().endpointUrl(", Endpoint.class);
332-
EndpointUrlCodeEmitter.emit(e.url(), builder, this);
333-
builder.add(")");
334-
e.headers().accept(this);
335-
e.properties().accept(this);
336-
builder.addStatement(".build()");
331+
Map<String, RuleExpression> properties = e.properties().properties();
332+
boolean hasHeaders = !e.headers().headers().isEmpty();
333+
boolean hasAuthSchemesOnly = !hasHeaders && properties.size() == 1 && properties.containsKey("authSchemes");
334+
boolean hasTwoAttrs = !hasHeaders && properties.size() == 2 && properties.containsKey("authSchemes");
335+
336+
if (hasAuthSchemesOnly) {
337+
// Optimized: Endpoint.ofAttribute(url, AUTH_SCHEMES, list)
338+
builder.add("return $T.ofAttribute(", Endpoint.class);
339+
EndpointUrlCodeEmitter.emit(e.url(), builder, this);
340+
builder.add(", $T.AUTH_SCHEMES, ", AwsEndpointAttribute.class);
341+
addAuthSchemesInlineValue(properties.get("authSchemes"));
342+
builder.addStatement(")");
343+
} else if (hasTwoAttrs) {
344+
// Optimized: Endpoint.ofAttributes(url, key1, val1, key2, val2)
345+
builder.add("return $T.ofAttributes(", Endpoint.class);
346+
EndpointUrlCodeEmitter.emit(e.url(), builder, this);
347+
for (Map.Entry<String, RuleExpression> entry : properties.entrySet()) {
348+
builder.add(", ");
349+
if ("authSchemes".equals(entry.getKey())) {
350+
builder.add("$T.AUTH_SCHEMES, ", AwsEndpointAttribute.class);
351+
addAuthSchemesInlineValue(entry.getValue());
352+
} else if (knownEndpointAttributes.containsKey(entry.getKey())) {
353+
KeyTypePair keyType = knownEndpointAttributes.get(entry.getKey());
354+
ClassConstant classConstant = parseClassConstant(keyType.getKey());
355+
builder.add("$T.$L, ", classConstant.className(), classConstant.fieldName());
356+
entry.getValue().accept(this);
357+
} else {
358+
builder.add("$T.AUTH_SCHEMES, ", AwsEndpointAttribute.class);
359+
entry.getValue().accept(this);
360+
}
361+
}
362+
builder.addStatement(")");
363+
} else {
364+
// General case: use builder pattern
365+
builder.add("return $T.builder().endpointUrl(", Endpoint.class);
366+
EndpointUrlCodeEmitter.emit(e.url(), builder, this);
367+
builder.add(")");
368+
e.headers().accept(this);
369+
e.properties().accept(this);
370+
builder.addStatement(".build()");
371+
}
337372
return null;
338373
}
339374

375+
private void addAuthSchemesInlineValue(RuleExpression authSchemesExpr) {
376+
ListExpression expr = (ListExpression) authSchemesExpr;
377+
builder.add("$T.asList(", Arrays.class);
378+
boolean isFirst = true;
379+
for (RuleExpression authSchemeExpr : expr.expressions()) {
380+
if (!isFirst) {
381+
builder.add(", ");
382+
}
383+
addAuthSchemesBody(authSchemeExpr);
384+
isFirst = false;
385+
}
386+
builder.add(")");
387+
}
388+
340389
@Override
341390
public Void visitPropertiesExpression(PropertiesExpression e) {
342391
Map<String, RuleExpression> properties = e.properties();

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/bdd/BddResultCodeGeneratorVisitor.java

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -252,16 +252,65 @@ public RuleType visitListExpression(ListExpression e) {
252252

253253
@Override
254254
public RuleType visitEndpointExpression(EndpointExpression e) {
255-
// Return Endpoint directly — no RuleResult wrapper
256-
builder.add("return $T.builder().endpointUrl(", Endpoint.class);
257-
EndpointUrlCodeEmitter.emit(e.url(), builder, this);
258-
builder.add(")");
259-
e.headers().accept(this);
260-
e.properties().accept(this);
261-
builder.addStatement(".build()");
255+
Map<String, RuleExpression> properties = e.properties().properties();
256+
boolean hasHeaders = !e.headers().headers().isEmpty();
257+
boolean hasAuthSchemesOnly = !hasHeaders && properties.size() == 1 && properties.containsKey("authSchemes");
258+
boolean hasTwoAttrs = !hasHeaders && properties.size() == 2 && properties.containsKey("authSchemes");
259+
260+
if (hasAuthSchemesOnly) {
261+
// Optimized: Endpoint.ofAttribute(url, AUTH_SCHEMES, list)
262+
builder.add("return $T.ofAttribute(", Endpoint.class);
263+
EndpointUrlCodeEmitter.emit(e.url(), builder, this);
264+
builder.add(", $T.AUTH_SCHEMES, ", AwsEndpointAttribute.class);
265+
addAuthSchemesValue(properties.get("authSchemes"));
266+
builder.addStatement(")");
267+
} else if (hasTwoAttrs) {
268+
// Optimized: Endpoint.ofAttributes(url, key1, val1, key2, val2)
269+
builder.add("return $T.ofAttributes(", Endpoint.class);
270+
EndpointUrlCodeEmitter.emit(e.url(), builder, this);
271+
for (Map.Entry<String, RuleExpression> entry : properties.entrySet()) {
272+
builder.add(", ");
273+
if ("authSchemes".equals(entry.getKey())) {
274+
builder.add("$T.AUTH_SCHEMES, ", AwsEndpointAttribute.class);
275+
addAuthSchemesValue(entry.getValue());
276+
} else if (knownEndpointAttributes.containsKey(entry.getKey())) {
277+
KeyTypePair keyType = knownEndpointAttributes.get(entry.getKey());
278+
ClassConstant classConstant = parseClassConstant(keyType.getKey());
279+
builder.add("$T.$L, ", classConstant.className(), classConstant.fieldName());
280+
entry.getValue().accept(this);
281+
} else {
282+
// Unknown attribute — use AwsEndpointAttribute for safety
283+
builder.add("$T.AUTH_SCHEMES, ", AwsEndpointAttribute.class);
284+
entry.getValue().accept(this);
285+
}
286+
}
287+
builder.addStatement(")");
288+
} else {
289+
// General case: use builder pattern
290+
builder.add("return $T.builder().endpointUrl(", Endpoint.class);
291+
EndpointUrlCodeEmitter.emit(e.url(), builder, this);
292+
builder.add(")");
293+
e.headers().accept(this);
294+
e.properties().accept(this);
295+
builder.addStatement(".build()");
296+
}
262297
return null;
263298
}
264299

300+
private void addAuthSchemesValue(RuleExpression authSchemesExpr) {
301+
ListExpression expr = (ListExpression) authSchemesExpr;
302+
builder.add("$T.asList(", Arrays.class);
303+
boolean isFirst = true;
304+
for (RuleExpression authSchemeExpr : expr.expressions()) {
305+
if (!isFirst) {
306+
builder.add(", ");
307+
}
308+
addAuthSchemesBody(authSchemeExpr);
309+
isFirst = false;
310+
}
311+
builder.add(")");
312+
}
313+
265314
@Override
266315
public RuleType visitPropertiesExpression(PropertiesExpression e) {
267316
Map<String, RuleExpression> properties = e.properties();

codegen/src/main/resources/software/amazon/awssdk/codegen/rules2/RulesFunctions.java.resource

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,21 @@ public class RulesFunctions {
2424
private static final int MAX_HOST_LABEL_SIZE = 63;
2525
private static final int MIN_BUCKET_SIZE = 3;
2626

27+
// Per-thread caches to avoid recomputing results for repeated calls with the same inputs.
28+
// These are common in endpoint resolution where the same region/bucket is resolved many times.
29+
private static final ThreadLocal<FunctionCache> CACHE = new ThreadLocal<FunctionCache>() {
30+
@Override
31+
protected FunctionCache initialValue() {
32+
return new FunctionCache();
33+
}
34+
};
35+
36+
private static final class FunctionCache {
37+
// awsPartition cache: (lastRegion -> lastPartition)
38+
String lastPartitionRegion;
39+
RulePartition lastPartitionResult;
40+
}
41+
2742
@SafeVarargs
2843
public static <T> T coalesce(T... args) {
2944
if (args == null || args.length < 2) {
@@ -191,6 +206,11 @@ public class RulesFunctions {
191206
// AWS related functions
192207

193208
public static RulePartition awsPartition(String regionName) {
209+
FunctionCache cache = CACHE.get();
210+
if (regionName != null && regionName.equals(cache.lastPartitionRegion)) {
211+
return cache.lastPartitionResult;
212+
}
213+
194214
PartitionData data = PARTITION_DATA.value();
195215
Partition matchedPartition;
196216

@@ -211,7 +231,10 @@ public class RulesFunctions {
211231
matchedPartition = AWS_PARTITION.value();
212232
}
213233

214-
return RulePartition.from(matchedPartition.id(), matchedPartition.outputs());
234+
RulePartition result = RulePartition.from(matchedPartition.id(), matchedPartition.outputs());
235+
cache.lastPartitionRegion = regionName;
236+
cache.lastPartitionResult = result;
237+
return result;
215238
}
216239

217240
public static RuleArn awsParseArn(String value) {

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules2/endpoint-provider-class.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private static Endpoint endpointRule1(QueryEndpointParams params, String region)
6464
if (parsedArn != null) {
6565
String arnResourceId = RulesFunctions.listAccess(parsedArn.resourceId(), 0);
6666
if (arnResourceId != null) {
67-
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", arnResourceId + "." + params.endpointId() + ".query." + partitionResult.dualStackDnsSuffix(), -1, "")).putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build())).build();
67+
return Endpoint.ofAttribute(EndpointUrl.fromComponents("https", arnResourceId + "." + params.endpointId() + ".query." + partitionResult.dualStackDnsSuffix(), -1, ""), AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build()));
6868
}
6969
}
7070
}
@@ -79,9 +79,9 @@ private static Endpoint endpointRule2(QueryEndpointParams params, RulePartition
7979
throw SdkClientException.create("FIPS endpoints not supported with multi-region endpoints");
8080
}
8181
if (params.useFipsEndpoint() == null && params.useDualStackEndpoint() != null && params.useDualStackEndpoint()) {
82-
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", params.endpointId() + ".query." + partitionResult.dualStackDnsSuffix(), -1, "")).putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build())).build();
82+
return Endpoint.ofAttribute(EndpointUrl.fromComponents("https", params.endpointId() + ".query." + partitionResult.dualStackDnsSuffix(), -1, ""), AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build()));
8383
}
84-
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", params.endpointId() + ".query." + partitionResult.dnsSuffix(), -1, "")).putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build())).build();
84+
return Endpoint.ofAttribute(EndpointUrl.fromComponents("https", params.endpointId() + ".query." + partitionResult.dnsSuffix(), -1, ""), AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build()));
8585
}
8686
return null;
8787
}
@@ -90,13 +90,13 @@ private static Endpoint endpointRule6(QueryEndpointParams params, String region,
9090
RulePartition partitionResult) {
9191
if (RulesFunctions.isValidHostLabel(region, false)) {
9292
if (params.useFipsEndpoint() != null && params.useFipsEndpoint() && params.useDualStackEndpoint() == null) {
93-
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", "query-fips." + region + "." + partitionResult.dnsSuffix(), -1, "")).putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build())).build();
93+
return Endpoint.ofAttribute(EndpointUrl.fromComponents("https", "query-fips." + region + "." + partitionResult.dnsSuffix(), -1, ""), AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build()));
9494
}
9595
if (params.useDualStackEndpoint() != null && params.useDualStackEndpoint() && params.useFipsEndpoint() == null) {
96-
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", "query." + region + "." + partitionResult.dualStackDnsSuffix(), -1, "")).putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build(), SigV4AuthScheme.builder().signingName("query").signingRegion(region).build())).build();
96+
return Endpoint.ofAttribute(EndpointUrl.fromComponents("https", "query." + region + "." + partitionResult.dualStackDnsSuffix(), -1, ""), AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build(), SigV4AuthScheme.builder().signingName("query").signingRegion(region).build()));
9797
}
9898
if (params.useDualStackEndpoint() != null && params.useFipsEndpoint() != null && params.useDualStackEndpoint() && params.useFipsEndpoint()) {
99-
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", "query-fips." + region + "." + partitionResult.dualStackDnsSuffix(), -1, "")).putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build())).build();
99+
return Endpoint.ofAttribute(EndpointUrl.fromComponents("https", "query-fips." + region + "." + partitionResult.dualStackDnsSuffix(), -1, ""), AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build()));
100100
}
101101
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", "query." + region + "." + partitionResult.dnsSuffix(), -1, "")).build();
102102
}

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules2/endpoint-provider-know-prop-override-class.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ private static Endpoint endpointRule1(QueryEndpointParams params, String region)
6464
if (parsedArn != null) {
6565
String arnResourceId = RulesFunctions.listAccess(parsedArn.resourceId(), 0);
6666
if (arnResourceId != null) {
67-
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", arnResourceId + "." + params.endpointId() + ".query." + partitionResult.dualStackDnsSuffix(), -1, "")).putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build())).build();
67+
return Endpoint.ofAttribute(EndpointUrl.fromComponents("https", arnResourceId + "." + params.endpointId() + ".query." + partitionResult.dualStackDnsSuffix(), -1, ""), AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build()));
6868
}
6969
}
7070
}
@@ -79,9 +79,9 @@ private static Endpoint endpointRule2(QueryEndpointParams params, RulePartition
7979
throw SdkClientException.create("FIPS endpoints not supported with multi-region endpoints");
8080
}
8181
if (params.useFipsEndpoint() == null && params.useDualStackEndpoint() != null && params.useDualStackEndpoint()) {
82-
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", params.endpointId() + ".query." + partitionResult.dualStackDnsSuffix(), -1, "")).putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build())).build();
82+
return Endpoint.ofAttribute(EndpointUrl.fromComponents("https", params.endpointId() + ".query." + partitionResult.dualStackDnsSuffix(), -1, ""), AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build()));
8383
}
84-
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", params.endpointId() + ".query." + partitionResult.dnsSuffix(), -1, "")).putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build())).build();
84+
return Endpoint.ofAttribute(EndpointUrl.fromComponents("https", params.endpointId() + ".query." + partitionResult.dnsSuffix(), -1, ""), AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build()));
8585
}
8686
return null;
8787
}
@@ -90,13 +90,13 @@ private static Endpoint endpointRule6(QueryEndpointParams params, String region,
9090
RulePartition partitionResult) {
9191
if (RulesFunctions.isValidHostLabel(region, false)) {
9292
if (params.useFipsEndpoint() != null && params.useFipsEndpoint() && params.useDualStackEndpoint() == null) {
93-
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", "query-fips." + region + "." + partitionResult.dnsSuffix(), -1, "")).putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build())).build();
93+
return Endpoint.ofAttribute(EndpointUrl.fromComponents("https", "query-fips." + region + "." + partitionResult.dnsSuffix(), -1, ""), AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build()));
9494
}
9595
if (params.useDualStackEndpoint() != null && params.useDualStackEndpoint() && params.useFipsEndpoint() == null) {
96-
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", "query." + region + "." + partitionResult.dualStackDnsSuffix(), -1, "")).putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build(), SigV4AuthScheme.builder().signingName("query").signingRegion(region).build())).build();
96+
return Endpoint.ofAttribute(EndpointUrl.fromComponents("https", "query." + region + "." + partitionResult.dualStackDnsSuffix(), -1, ""), AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build(), SigV4AuthScheme.builder().signingName("query").signingRegion(region).build()));
9797
}
9898
if (params.useDualStackEndpoint() != null && params.useFipsEndpoint() != null && params.useDualStackEndpoint() && params.useFipsEndpoint()) {
99-
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", "query-fips." + region + "." + partitionResult.dualStackDnsSuffix(), -1, "")).putAttribute(AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build())).build();
99+
return Endpoint.ofAttribute(EndpointUrl.fromComponents("https", "query-fips." + region + "." + partitionResult.dualStackDnsSuffix(), -1, ""), AwsEndpointAttribute.AUTH_SCHEMES, Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*")).build()));
100100
}
101101
return Endpoint.builder().endpointUrl(EndpointUrl.fromComponents("https", "query." + region + "." + partitionResult.dnsSuffix(), -1, "")).build();
102102
}

0 commit comments

Comments
 (0)