Skip to content

Commit 004a000

Browse files
jeet1995Copilot
andauthored
Fix JVM <clinit> deadlock by removing static final accessor fields (#48689)
* Fix JVM <clinit> deadlock and CosmosItemSerializer.DEFAULT_SERIALIZER null Fixes #48622, #48585 Replace all static final accessor fields and inline ImplementationBridgeHelpers calls with uniform private static getter methods. This eliminates <clinit>-time class loading that caused permanent deadlocks under concurrent class initialization (JLS 12.4.2). Fix CosmosItemSerializer.DEFAULT_SERIALIZER circular <clinit> — create instance directly and move INTERNAL_DEFAULT_SERIALIZER to parent class to prevent concurrent <clinit> between parent and child. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix review comments: remove duplicate imports, add getter pattern - ModelBridgeInternal.java: Remove 26 duplicate ImplementationBridgeHelpers imports - ItemBulkOperation.java: Remove 2 duplicate ImplementationBridgeHelpers imports - SqlQuerySpecWithEncryption.java: Add private static internalDefaultSerializer() getter (matching uniform pattern), replace inline accessor calls, remove unused DefaultCosmosItemSerializer import Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix misleading JavaDoc on allAccessorClassesMustHaveStaticInitializerBlock test Clarify that the test verifies accessor resolvability (via <clinit> or initializeAllAccessors fallback), not that each class independently registers its accessor. Structural enforcement is done by the companion noStaticOrInstanceAccessorFieldsInConsumingClasses test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Refactoring. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d9b8c1a commit 004a000

85 files changed

Lines changed: 1508 additions & 923 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

sdk/cosmos/azure-cosmos-encryption/src/main/java/com/azure/cosmos/encryption/models/SqlQuerySpecWithEncryption.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
import com.azure.cosmos.encryption.CosmosEncryptionAsyncContainer;
88
import com.azure.cosmos.encryption.implementation.Constants;
99
import com.azure.cosmos.encryption.implementation.EncryptionImplementationBridgeHelpers;
10+
import com.azure.cosmos.implementation.ImplementationBridgeHelpers;
1011
import com.azure.cosmos.encryption.implementation.EncryptionProcessor;
1112
import com.azure.cosmos.encryption.implementation.EncryptionUtils;
1213
import com.azure.cosmos.encryption.implementation.mdesrc.cryptography.EncryptionType;
1314
import com.azure.cosmos.encryption.implementation.mdesrc.cryptography.MicrosoftDataEncryptionException;
14-
import com.azure.cosmos.implementation.DefaultCosmosItemSerializer;
1515
import com.azure.cosmos.implementation.Utils;
1616
import com.azure.cosmos.implementation.apachecommons.lang.StringUtils;
1717
import com.azure.cosmos.implementation.apachecommons.lang.tuple.Pair;
@@ -33,6 +33,11 @@ public final class SqlQuerySpecWithEncryption {
3333
private final HashMap<String, SqlParameter> encryptionParamMap = new HashMap<>();
3434
private final EncryptionImplementationBridgeHelpers.CosmosEncryptionAsyncContainerHelper.CosmosEncryptionAsyncContainerAccessor cosmosEncryptionAsyncContainerAccessor = EncryptionImplementationBridgeHelpers.CosmosEncryptionAsyncContainerHelper.getCosmosEncryptionAsyncContainerAccessor();
3535

36+
private static CosmosItemSerializer internalDefaultSerializer() {
37+
return ImplementationBridgeHelpers.CosmosItemSerializerHelper
38+
.getCosmosItemSerializerAccessor().getInternalDefaultSerializer();
39+
}
40+
3641
/**
3742
* Creates a new instance of SQL query spec with encryption.
3843
*
@@ -86,10 +91,10 @@ Mono<Void> addEncryptionParameterAsync(String path, SqlParameter sqlParameter,
8691
}
8792
byte[] valueByte =
8893
EncryptionUtils.serializeJsonToByteArray(
89-
DefaultCosmosItemSerializer.INTERNAL_DEFAULT_SERIALIZER,
94+
internalDefaultSerializer(),
9095
sqlParameter.getValue(Object.class));
9196
JsonNode itemJObj = Utils.parse(
92-
valueByte, JsonNode.class, DefaultCosmosItemSerializer.INTERNAL_DEFAULT_SERIALIZER);
97+
valueByte, JsonNode.class, internalDefaultSerializer());
9398
Pair<EncryptionProcessor.TypeMarker, byte[]> typeMarkerPair =
9499
EncryptionProcessor.toByteArray(itemJObj);
95100
byte[] cipherText =

sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/ImplementationBridgeHelpersTest.java

Lines changed: 506 additions & 0 deletions
Large diffs are not rendered by default.

sdk/cosmos/azure-cosmos/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#### Bugs Fixed
1010
* Fixed an issue where change feed with `startFrom` point-in-time returned `400` on merged partitions by enabling the `CHANGE_FEED_WITH_START_TIME_POST_MERGE` SDK capability.
11+
* Fixed JVM `<clinit>` deadlock when multiple threads concurrently trigger Cosmos SDK class loading for the first time. - See [PR 48689](https://github.com/Azure/azure-sdk-for-java/pull/48689)
1112

1213
#### Other Changes
1314

sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosAsyncClient.java

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -79,22 +79,25 @@
7979
builder = CosmosClientBuilder.class,
8080
isAsync = true)
8181
public final class CosmosAsyncClient implements Closeable {
82+
private static ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.CosmosClientTelemetryConfigAccessor clientTelemetryConfigAccessor() {
83+
return ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor();
84+
}
85+
86+
private static ImplementationBridgeHelpers.ReadConsistencyStrategyHelper.ReadConsistencyStrategyAccessor readConsistencyStrategyAccessor() {
87+
return ImplementationBridgeHelpers.ReadConsistencyStrategyHelper.getReadConsistencyStrategyAccessor();
88+
}
89+
90+
private static ImplementationBridgeHelpers.CosmosQueryRequestOptionsHelper.CosmosQueryRequestOptionsAccessor queryOptionsAccessor() {
91+
return ImplementationBridgeHelpers.CosmosQueryRequestOptionsHelper.getCosmosQueryRequestOptionsAccessor();
92+
}
93+
94+
private static ImplementationBridgeHelpers.FeedResponseHelper.FeedResponseAccessor feedResponseAccessor() {
95+
return ImplementationBridgeHelpers.FeedResponseHelper.getFeedResponseAccessor();
96+
}
97+
8298
private static final Logger logger = LoggerFactory.getLogger(CosmosAsyncClient.class);
8399

84100
private static final CosmosClientTelemetryConfig DEFAULT_TELEMETRY_CONFIG = new CosmosClientTelemetryConfig();
85-
private static final ImplementationBridgeHelpers.CosmosQueryRequestOptionsHelper.CosmosQueryRequestOptionsAccessor queryOptionsAccessor =
86-
ImplementationBridgeHelpers.CosmosQueryRequestOptionsHelper.getCosmosQueryRequestOptionsAccessor();
87-
private static final ImplementationBridgeHelpers.FeedResponseHelper.FeedResponseAccessor feedResponseAccessor =
88-
ImplementationBridgeHelpers.FeedResponseHelper.getFeedResponseAccessor();
89-
private static final ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.CosmosClientTelemetryConfigAccessor
90-
telemetryConfigAccessor = ImplementationBridgeHelpers
91-
.CosmosClientTelemetryConfigHelper
92-
.getCosmosClientTelemetryConfigAccessor();
93-
94-
private static final ImplementationBridgeHelpers.ReadConsistencyStrategyHelper.ReadConsistencyStrategyAccessor
95-
readConsistencyStrategyAccessor = ImplementationBridgeHelpers
96-
.ReadConsistencyStrategyHelper
97-
.getReadConsistencyStrategyAccessor();
98101

99102
private final static Function<CosmosAsyncContainer, CosmosAsyncContainer> DEFAULT_CONTAINER_FACTORY =
100103
(originalContainer) -> originalContainer;
@@ -148,7 +151,7 @@ public final class CosmosAsyncClient implements Closeable {
148151
this.clientTelemetryConfig = effectiveTelemetryConfig;
149152
boolean contentResponseOnWriteEnabled = builder.isContentResponseOnWriteEnabled();
150153
ApiType apiType = builder.apiType();
151-
String clientCorrelationId = telemetryConfigAccessor
154+
String clientCorrelationId = clientTelemetryConfigAccessor()
152155
.getClientCorrelationId(effectiveTelemetryConfig);
153156

154157
List<Permission> permissionList = new ArrayList<>();
@@ -207,15 +210,14 @@ public final class CosmosAsyncClient implements Closeable {
207210
TagName.ClientCorrelationId.toString(),
208211
ClientTelemetryMetrics.escape(effectiveClientCorrelationId));
209212

210-
this.clientMetricRegistrySnapshot = telemetryConfigAccessor
213+
this.clientMetricRegistrySnapshot = clientTelemetryConfigAccessor()
211214
.getClientMetricRegistry(effectiveTelemetryConfig);
212215

213-
CosmosMeterOptions cpuMeterOptions = telemetryConfigAccessor
216+
CosmosMeterOptions cpuMeterOptions = clientTelemetryConfigAccessor()
214217
.getMeterOptions(effectiveTelemetryConfig, CosmosMetricName.SYSTEM_CPU);
215-
CosmosMeterOptions memoryMeterOptions = telemetryConfigAccessor
218+
CosmosMeterOptions memoryMeterOptions = clientTelemetryConfigAccessor()
216219
.getMeterOptions(effectiveTelemetryConfig, CosmosMetricName.SYSTEM_MEMORY_FREE);
217220

218-
219221
if (clientMetricRegistrySnapshot != null) {
220222
ClientTelemetryMetrics.add(clientMetricRegistrySnapshot, cpuMeterOptions, memoryMeterOptions);
221223
}
@@ -224,15 +226,15 @@ public final class CosmosAsyncClient implements Closeable {
224226
);
225227

226228
if (this.clientMetricRegistrySnapshot != null) {
227-
telemetryConfigAccessor.setClientCorrelationTag(
229+
clientTelemetryConfigAccessor().setClientCorrelationTag(
228230
effectiveTelemetryConfig,
229231
this.clientCorrelationTag );
230-
telemetryConfigAccessor.setAccountName(
232+
clientTelemetryConfigAccessor().setAccountName(
231233
effectiveTelemetryConfig,
232234
this.accountTagValue
233235
);
234236

235-
telemetryConfigAccessor.addDiagnosticsHandler(
237+
clientTelemetryConfigAccessor().addDiagnosticsHandler(
236238
effectiveTelemetryConfig,
237239
new ClientMetricsDiagnosticsHandler(this)
238240
);
@@ -475,7 +477,7 @@ CosmosPagedFlux<CosmosDatabaseProperties> readAllDatabases(CosmosQueryRequestOpt
475477
null,
476478
ResourceType.Database,
477479
OperationType.ReadFeed,
478-
queryOptionsAccessor.getQueryNameOrDefault(nonNullOptions, spanName),
480+
queryOptionsAccessor().getQueryNameOrDefault(nonNullOptions, spanName),
479481
nonNullOptions,
480482
pagedFluxOptions
481483
);
@@ -484,7 +486,7 @@ CosmosPagedFlux<CosmosDatabaseProperties> readAllDatabases(CosmosQueryRequestOpt
484486

485487
return getDocClientWrapper().readDatabases(state)
486488
.map(response ->
487-
feedResponseAccessor.createFeedResponse(
489+
feedResponseAccessor().createFeedResponse(
488490
ModelBridgeInternal.getCosmosDatabasePropertiesFromV2Results(response.getResults()),
489491
response.getResponseHeaders(),
490492
response.getCosmosDiagnostics()));
@@ -504,7 +506,6 @@ public CosmosPagedFlux<CosmosDatabaseProperties> readAllDatabases() {
504506
return readAllDatabases(new CosmosQueryRequestOptions());
505507
}
506508

507-
508509
/**
509510
* Query for databases.
510511
* <br/>
@@ -662,22 +663,21 @@ private CosmosPagedFlux<CosmosDatabaseProperties> queryDatabasesInternal(
662663
null,
663664
ResourceType.Database,
664665
OperationType.Query,
665-
queryOptionsAccessor.getQueryNameOrDefault(nonNullOptions, spanName),
666+
queryOptionsAccessor().getQueryNameOrDefault(nonNullOptions, spanName),
666667
nonNullOptions,
667668
pagedFluxOptions
668669
);
669670

670671
pagedFluxOptions.setFeedOperationState(state);
671672

672673
return getDocClientWrapper().queryDatabases(querySpec, state)
673-
.map(response -> feedResponseAccessor.createFeedResponse(
674+
.map(response -> feedResponseAccessor().createFeedResponse(
674675
ModelBridgeInternal.getCosmosDatabasePropertiesFromV2Results(response.getResults()),
675676
response.getResponseHeaders(),
676677
response.getCosmosDiagnostics()));
677678
});
678679
}
679680

680-
681681
private Mono<CosmosDatabaseResponse> createDatabaseIfNotExistsInternal(CosmosAsyncDatabase database,
682682
ThroughputProperties throughputProperties, Context context) {
683683
String spanName = "createDatabaseIfNotExists." + database.getId();
@@ -765,7 +765,7 @@ ReadConsistencyStrategy getEffectiveReadConsistencyStrategy(
765765
OperationType operationType,
766766
ReadConsistencyStrategy desiredReadConsistencyStrategyOfOperation) {
767767

768-
return readConsistencyStrategyAccessor.getEffectiveReadConsistencyStrategy(
768+
return readConsistencyStrategyAccessor().getEffectiveReadConsistencyStrategy(
769769
resourceType,
770770
operationType,
771771
desiredReadConsistencyStrategyOfOperation,
@@ -780,13 +780,12 @@ CosmosDiagnosticsThresholds getEffectiveDiagnosticsThresholds(
780780
return operationLevelThresholds;
781781
}
782782

783-
784783
if (this.clientTelemetryConfig == null) {
785784
return new CosmosDiagnosticsThresholds();
786785
}
787786

788787
CosmosDiagnosticsThresholds clientLevelThresholds =
789-
telemetryConfigAccessor.getDiagnosticsThresholds(this.clientTelemetryConfig);
788+
clientTelemetryConfigAccessor().getDiagnosticsThresholds(this.clientTelemetryConfig);
790789

791790
return clientLevelThresholds != null ? clientLevelThresholds : new CosmosDiagnosticsThresholds();
792791
}
@@ -805,15 +804,15 @@ boolean isTransportLevelTracingEnabled() {
805804
this.clientTelemetryConfig
806805
: DEFAULT_TELEMETRY_CONFIG;
807806

808-
if (telemetryConfigAccessor.isLegacyTracingEnabled(effectiveConfig)) {
807+
if (clientTelemetryConfigAccessor().isLegacyTracingEnabled(effectiveConfig)) {
809808
return false;
810809
}
811810

812811
if (this.getConnectionPolicy().getConnectionMode() != ConnectionMode.DIRECT) {
813812
return false;
814813
}
815814

816-
return telemetryConfigAccessor.isTransportLevelTracingEnabled(effectiveConfig);
815+
return clientTelemetryConfigAccessor().isTransportLevelTracingEnabled(effectiveConfig);
817816
}
818817

819818
void recordOpenConnectionsAndInitCachesCompleted(List<CosmosContainerIdentity> cosmosContainerIdentities) {
@@ -859,13 +858,13 @@ public String getAccountTagValue(CosmosAsyncClient client) {
859858

860859
@Override
861860
public EnumSet<TagName> getMetricTagNames(CosmosAsyncClient client) {
862-
return telemetryConfigAccessor
861+
return clientTelemetryConfigAccessor()
863862
.getMetricTagNames(client.clientTelemetryConfig);
864863
}
865864

866865
@Override
867866
public EnumSet<MetricCategory> getMetricCategories(CosmosAsyncClient client) {
868-
return telemetryConfigAccessor
867+
return clientTelemetryConfigAccessor()
869868
.getMetricCategories(client.clientTelemetryConfig);
870869
}
871870

@@ -896,7 +895,7 @@ public String getUserAgent(CosmosAsyncClient client) {
896895

897896
@Override
898897
public CosmosMeterOptions getMeterOptions(CosmosAsyncClient client, CosmosMetricName name) {
899-
return telemetryConfigAccessor
898+
return clientTelemetryConfigAccessor()
900899
.getMeterOptions(client.clientTelemetryConfig, name);
901900
}
902901

0 commit comments

Comments
 (0)