Skip to content

Commit 66afd43

Browse files
jeet1995Copilot
andcommitted
Fix JVM <clinit> deadlock by removing static final accessor fields
Remove all 49 'private static final XxxAccessor' fields from consuming classes and replace with inline calls to the getter. These fields triggered initializeAllAccessors() during <clinit>, creating circular class initialization dependencies that permanently deadlocked the JVM when multiple threads concurrently loaded Cosmos classes. The accessor is already cached inside ImplementationBridgeHelpers via AtomicReference — the static fields were a redundant cache. Inline calls add one volatile read (~1ns) per access, negligible vs the actual Cosmos operations. Also includes: - Missing static { initialize(); } blocks for 3 classes - CosmosItemSerializer <clinit> ordering fix - Renamed misnamed accessor in CosmosDiagnosticsThresholdsHelper - Forked-JVM deadlock regression test (invocationCount=5, 12 threads) - Behavioral enforcement test for static { initialize(); } blocks Fixes: #48622 Fixes: #48585 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3a6b4a5 commit 66afd43

File tree

54 files changed

+690
-600
lines changed

Some content is hidden

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

54 files changed

+690
-600
lines changed

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

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

sdk/cosmos/azure-cosmos/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
#### Breaking Changes
88

99
#### Bugs Fixed
10-
Fixing an NPE caused due to boxed Boolean conversion. - See [PR 48656](https://github.com/Azure/azure-sdk-for-java/pull/48656/)
10+
* Fixing an NPE caused due to boxed Boolean conversion. - See [PR 48656](https://github.com/Azure/azure-sdk-for-java/pull/48656/)
11+
* Fixed JVM `<clinit>` deadlock when multiple threads concurrently trigger Cosmos SDK class loading for the first time. - See [PR 48667](https://github.com/Azure/azure-sdk-for-java/pull/48667)
1112

1213
#### Other Changes
1314

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

Lines changed: 18 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,6 @@ public final class CosmosAsyncClient implements Closeable {
8282
private static final Logger logger = LoggerFactory.getLogger(CosmosAsyncClient.class);
8383

8484
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();
9885

9986
private final static Function<CosmosAsyncContainer, CosmosAsyncContainer> DEFAULT_CONTAINER_FACTORY =
10087
(originalContainer) -> originalContainer;
@@ -148,7 +135,7 @@ public final class CosmosAsyncClient implements Closeable {
148135
this.clientTelemetryConfig = effectiveTelemetryConfig;
149136
boolean contentResponseOnWriteEnabled = builder.isContentResponseOnWriteEnabled();
150137
ApiType apiType = builder.apiType();
151-
String clientCorrelationId = telemetryConfigAccessor
138+
String clientCorrelationId = ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor()
152139
.getClientCorrelationId(effectiveTelemetryConfig);
153140

154141
List<Permission> permissionList = new ArrayList<>();
@@ -207,15 +194,14 @@ public final class CosmosAsyncClient implements Closeable {
207194
TagName.ClientCorrelationId.toString(),
208195
ClientTelemetryMetrics.escape(effectiveClientCorrelationId));
209196

210-
this.clientMetricRegistrySnapshot = telemetryConfigAccessor
197+
this.clientMetricRegistrySnapshot = ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor()
211198
.getClientMetricRegistry(effectiveTelemetryConfig);
212199

213-
CosmosMeterOptions cpuMeterOptions = telemetryConfigAccessor
200+
CosmosMeterOptions cpuMeterOptions = ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor()
214201
.getMeterOptions(effectiveTelemetryConfig, CosmosMetricName.SYSTEM_CPU);
215-
CosmosMeterOptions memoryMeterOptions = telemetryConfigAccessor
202+
CosmosMeterOptions memoryMeterOptions = ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor()
216203
.getMeterOptions(effectiveTelemetryConfig, CosmosMetricName.SYSTEM_MEMORY_FREE);
217204

218-
219205
if (clientMetricRegistrySnapshot != null) {
220206
ClientTelemetryMetrics.add(clientMetricRegistrySnapshot, cpuMeterOptions, memoryMeterOptions);
221207
}
@@ -224,15 +210,15 @@ public final class CosmosAsyncClient implements Closeable {
224210
);
225211

226212
if (this.clientMetricRegistrySnapshot != null) {
227-
telemetryConfigAccessor.setClientCorrelationTag(
213+
ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor().setClientCorrelationTag(
228214
effectiveTelemetryConfig,
229215
this.clientCorrelationTag );
230-
telemetryConfigAccessor.setAccountName(
216+
ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor().setAccountName(
231217
effectiveTelemetryConfig,
232218
this.accountTagValue
233219
);
234220

235-
telemetryConfigAccessor.addDiagnosticsHandler(
221+
ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor().addDiagnosticsHandler(
236222
effectiveTelemetryConfig,
237223
new ClientMetricsDiagnosticsHandler(this)
238224
);
@@ -475,7 +461,7 @@ CosmosPagedFlux<CosmosDatabaseProperties> readAllDatabases(CosmosQueryRequestOpt
475461
null,
476462
ResourceType.Database,
477463
OperationType.ReadFeed,
478-
queryOptionsAccessor.getQueryNameOrDefault(nonNullOptions, spanName),
464+
ImplementationBridgeHelpers.CosmosQueryRequestOptionsHelper.getCosmosQueryRequestOptionsAccessor().getQueryNameOrDefault(nonNullOptions, spanName),
479465
nonNullOptions,
480466
pagedFluxOptions
481467
);
@@ -484,7 +470,7 @@ CosmosPagedFlux<CosmosDatabaseProperties> readAllDatabases(CosmosQueryRequestOpt
484470

485471
return getDocClientWrapper().readDatabases(state)
486472
.map(response ->
487-
feedResponseAccessor.createFeedResponse(
473+
ImplementationBridgeHelpers.FeedResponseHelper.getFeedResponseAccessor().createFeedResponse(
488474
ModelBridgeInternal.getCosmosDatabasePropertiesFromV2Results(response.getResults()),
489475
response.getResponseHeaders(),
490476
response.getCosmosDiagnostics()));
@@ -504,7 +490,6 @@ public CosmosPagedFlux<CosmosDatabaseProperties> readAllDatabases() {
504490
return readAllDatabases(new CosmosQueryRequestOptions());
505491
}
506492

507-
508493
/**
509494
* Query for databases.
510495
* <br/>
@@ -662,22 +647,21 @@ private CosmosPagedFlux<CosmosDatabaseProperties> queryDatabasesInternal(
662647
null,
663648
ResourceType.Database,
664649
OperationType.Query,
665-
queryOptionsAccessor.getQueryNameOrDefault(nonNullOptions, spanName),
650+
ImplementationBridgeHelpers.CosmosQueryRequestOptionsHelper.getCosmosQueryRequestOptionsAccessor().getQueryNameOrDefault(nonNullOptions, spanName),
666651
nonNullOptions,
667652
pagedFluxOptions
668653
);
669654

670655
pagedFluxOptions.setFeedOperationState(state);
671656

672657
return getDocClientWrapper().queryDatabases(querySpec, state)
673-
.map(response -> feedResponseAccessor.createFeedResponse(
658+
.map(response -> ImplementationBridgeHelpers.FeedResponseHelper.getFeedResponseAccessor().createFeedResponse(
674659
ModelBridgeInternal.getCosmosDatabasePropertiesFromV2Results(response.getResults()),
675660
response.getResponseHeaders(),
676661
response.getCosmosDiagnostics()));
677662
});
678663
}
679664

680-
681665
private Mono<CosmosDatabaseResponse> createDatabaseIfNotExistsInternal(CosmosAsyncDatabase database,
682666
ThroughputProperties throughputProperties, Context context) {
683667
String spanName = "createDatabaseIfNotExists." + database.getId();
@@ -765,7 +749,7 @@ ReadConsistencyStrategy getEffectiveReadConsistencyStrategy(
765749
OperationType operationType,
766750
ReadConsistencyStrategy desiredReadConsistencyStrategyOfOperation) {
767751

768-
return readConsistencyStrategyAccessor.getEffectiveReadConsistencyStrategy(
752+
return ImplementationBridgeHelpers.ReadConsistencyStrategyHelper.getReadConsistencyStrategyAccessor().getEffectiveReadConsistencyStrategy(
769753
resourceType,
770754
operationType,
771755
desiredReadConsistencyStrategyOfOperation,
@@ -780,13 +764,12 @@ CosmosDiagnosticsThresholds getEffectiveDiagnosticsThresholds(
780764
return operationLevelThresholds;
781765
}
782766

783-
784767
if (this.clientTelemetryConfig == null) {
785768
return new CosmosDiagnosticsThresholds();
786769
}
787770

788771
CosmosDiagnosticsThresholds clientLevelThresholds =
789-
telemetryConfigAccessor.getDiagnosticsThresholds(this.clientTelemetryConfig);
772+
ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor().getDiagnosticsThresholds(this.clientTelemetryConfig);
790773

791774
return clientLevelThresholds != null ? clientLevelThresholds : new CosmosDiagnosticsThresholds();
792775
}
@@ -805,15 +788,15 @@ boolean isTransportLevelTracingEnabled() {
805788
this.clientTelemetryConfig
806789
: DEFAULT_TELEMETRY_CONFIG;
807790

808-
if (telemetryConfigAccessor.isLegacyTracingEnabled(effectiveConfig)) {
791+
if (ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor().isLegacyTracingEnabled(effectiveConfig)) {
809792
return false;
810793
}
811794

812795
if (this.getConnectionPolicy().getConnectionMode() != ConnectionMode.DIRECT) {
813796
return false;
814797
}
815798

816-
return telemetryConfigAccessor.isTransportLevelTracingEnabled(effectiveConfig);
799+
return ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor().isTransportLevelTracingEnabled(effectiveConfig);
817800
}
818801

819802
void recordOpenConnectionsAndInitCachesCompleted(List<CosmosContainerIdentity> cosmosContainerIdentities) {
@@ -859,13 +842,13 @@ public String getAccountTagValue(CosmosAsyncClient client) {
859842

860843
@Override
861844
public EnumSet<TagName> getMetricTagNames(CosmosAsyncClient client) {
862-
return telemetryConfigAccessor
845+
return ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor()
863846
.getMetricTagNames(client.clientTelemetryConfig);
864847
}
865848

866849
@Override
867850
public EnumSet<MetricCategory> getMetricCategories(CosmosAsyncClient client) {
868-
return telemetryConfigAccessor
851+
return ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor()
869852
.getMetricCategories(client.clientTelemetryConfig);
870853
}
871854

@@ -896,7 +879,7 @@ public String getUserAgent(CosmosAsyncClient client) {
896879

897880
@Override
898881
public CosmosMeterOptions getMeterOptions(CosmosAsyncClient client, CosmosMetricName name) {
899-
return telemetryConfigAccessor
882+
return ImplementationBridgeHelpers.CosmosClientTelemetryConfigHelper.getCosmosClientTelemetryConfigAccessor()
900883
.getMeterOptions(client.clientTelemetryConfig, name);
901884
}
902885

0 commit comments

Comments
 (0)