33
44package com .azure .cosmos .kafka .connect ;
55
6- import com .azure .cosmos .CosmosAsyncClient ;
76import com .azure .cosmos .CosmosAsyncContainer ;
87import com .azure .cosmos .CosmosAsyncDatabase ;
98import com .azure .cosmos .implementation .ImplementationBridgeHelpers ;
109import com .azure .cosmos .implementation .UUIDs ;
1110import com .azure .cosmos .implementation .apachecommons .lang .RandomUtils ;
1211import com .azure .cosmos .implementation .apachecommons .lang .StringUtils ;
1312import com .azure .cosmos .implementation .apachecommons .lang .tuple .Pair ;
14- import com .azure .cosmos .kafka .connect .implementation .CosmosClientStore ;
13+ import com .azure .cosmos .kafka .connect .implementation .CosmosClientCache ;
14+ import com .azure .cosmos .kafka .connect .implementation .CosmosClientCacheItem ;
1515import com .azure .cosmos .kafka .connect .implementation .CosmosMasterKeyAuthConfig ;
1616import com .azure .cosmos .kafka .connect .implementation .CosmosThroughputControlConfig ;
1717import com .azure .cosmos .kafka .connect .implementation .KafkaCosmosConstants ;
@@ -74,7 +74,7 @@ public final class CosmosSourceConnector extends SourceConnector implements Auto
7474 private static final int METADATA_CONTAINER_DEFAULT_RU_CONFIG = 4000 ;
7575
7676 private CosmosSourceConfig config ;
77- private CosmosAsyncClient cosmosClient ;
77+ private CosmosClientCacheItem cosmosClientItem ;
7878 private MetadataMonitorThread monitorThread ;
7979 private MetadataKafkaStorageManager kafkaOffsetStorageReader ;
8080 private IMetadataReader metadataReader ;
@@ -88,28 +88,37 @@ public ExactlyOnceSupport exactlyOnceSupport(Map<String, String> connectorConfig
8888 @ Override
8989 public void start (Map <String , String > props ) {
9090 LOGGER .info ("Starting the kafka cosmos source connector" );
91- this .config = new CosmosSourceConfig (props );
92- this .connectorName = props .containsKey (CONNECTOR_NAME ) ? props .get (CONNECTOR_NAME ).toString () : "EMPTY" ;
93- this .cosmosClient = CosmosClientStore .getCosmosClient (this .config .getAccountConfig (), connectorName );
94- CosmosSourceContainersConfig containersConfig = this .config .getContainersConfig ();
95- validateDatabaseAndContainers (
96- containersConfig .getIncludedContainers (),
97- this .cosmosClient ,
98- containersConfig .getDatabaseName ());
99-
100- // IMPORTANT: sequence matters
101- this .kafkaOffsetStorageReader = new MetadataKafkaStorageManager (this .context ().offsetStorageReader ());
102- this .metadataReader = this .getMetadataReader ();
103- this .monitorThread = new MetadataMonitorThread (
104- connectorName ,
105- this .config .getContainersConfig (),
106- this .config .getMetadataConfig (),
107- this .context (),
108- this .metadataReader ,
109- this .cosmosClient
110- );
111-
112- this .monitorThread .start ();
91+ try {
92+ this .config = new CosmosSourceConfig (props );
93+ this .connectorName = props .containsKey (CONNECTOR_NAME ) ? props .get (CONNECTOR_NAME ).toString () : "EMPTY" ;
94+ this .cosmosClientItem = CosmosClientCache .getCosmosClient (this .config .getAccountConfig (), connectorName );
95+ CosmosSourceContainersConfig containersConfig = this .config .getContainersConfig ();
96+ validateDatabaseAndContainers (
97+ containersConfig .getIncludedContainers (),
98+ this .cosmosClientItem .getClient (),
99+ containersConfig .getDatabaseName ());
100+
101+ // IMPORTANT: sequence matters
102+ this .kafkaOffsetStorageReader = new MetadataKafkaStorageManager (this .context ().offsetStorageReader ());
103+ this .metadataReader = this .getMetadataReader ();
104+ this .monitorThread = new MetadataMonitorThread (
105+ connectorName ,
106+ this .config .getContainersConfig (),
107+ this .config .getMetadataConfig (),
108+ this .context (),
109+ this .metadataReader ,
110+ this .cosmosClientItem .getClient ()
111+ );
112+
113+ this .monitorThread .start ();
114+ } catch (Exception e ) {
115+ // if the connector failed to start, release initialized resources here
116+ LOGGER .warn ("Error starting the kafka cosmos sink connector" , e );
117+ this .cleanup ();
118+ // re-throw the exception back to kafka
119+ throw e ;
120+ }
121+
113122 }
114123
115124 @ Override
@@ -151,18 +160,26 @@ public List<Map<String, String>> taskConfigs(int maxTasks) {
151160 return taskConfigs ;
152161 }
153162
154- @ Override
155- public void stop () {
156- LOGGER .info ("Stopping Kafka CosmosDB source connector" );
157- if (this .cosmosClient != null ) {
158- LOGGER .debug ("Closing cosmos client" );
159- this .cosmosClient .close ();
160- }
163+ private void cleanup () {
164+ LOGGER .info ("Cleaning up CosmosSourceConnector" );
161165
166+ // Close monitor thread first since it uses the cosmos client
162167 if (this .monitorThread != null ) {
163168 LOGGER .debug ("Closing monitoring thread" );
164169 this .monitorThread .close ();
165170 }
171+
172+ if (this .cosmosClientItem != null ) {
173+ LOGGER .debug ("Releasing cosmos client" );
174+ CosmosClientCache .releaseCosmosClient (this .cosmosClientItem .getClientConfig ());
175+ this .cosmosClientItem = null ;
176+ }
177+ }
178+
179+ @ Override
180+ public void stop () {
181+ LOGGER .info ("Stopping Kafka CosmosDB source connector" );
182+ cleanup ();
166183 }
167184
168185 @ Override
@@ -181,7 +198,8 @@ private IMetadataReader getMetadataReader() {
181198 return this .kafkaOffsetStorageReader ;
182199 case COSMOS :
183200 CosmosAsyncContainer metadataContainer =
184- this .cosmosClient
201+ this .cosmosClientItem
202+ .getClient ()
185203 .getDatabase (this .config .getContainersConfig ().getDatabaseName ())
186204 .getContainer (this .config .getMetadataConfig ().getStorageName ());
187205 // validate the metadata container config
@@ -230,7 +248,8 @@ private Mono<CosmosContainerResponse> createMetadataContainer() {
230248 }
231249
232250 private Mono <CosmosContainerResponse > createMetadataContainer (Integer throughput ) {
233- return this .cosmosClient
251+ return this .cosmosClientItem
252+ .getClient ()
234253 .getDatabase (this .config .getContainersConfig ().getDatabaseName ())
235254 .createContainer (
236255 this .config .getMetadataConfig ().getStorageName (),
@@ -363,7 +382,10 @@ private Map<FeedRange, KafkaCosmosChangeFeedState> getEffectiveFeedRangesContinu
363382 .block ().v ;
364383
365384 Map <FeedRange , KafkaCosmosChangeFeedState > effectiveFeedRangesContinuationMap = new LinkedHashMap <>();
366- CosmosAsyncContainer container = this .cosmosClient .getDatabase (databaseName ).getContainer (containerProperties .getId ());
385+ CosmosAsyncContainer container =
386+ this .cosmosClientItem
387+ .getClient ()
388+ .getDatabase (databaseName ).getContainer (containerProperties .getId ());
367389
368390 Flux .fromIterable (containerFeedRanges )
369391 .flatMap (containerFeedRange -> {
@@ -493,7 +515,8 @@ private KafkaCosmosChangeFeedState getContinuationStateFromOffset(
493515 }
494516
495517 private List <FeedRange > getFeedRanges (CosmosContainerProperties containerProperties ) {
496- return this .cosmosClient
518+ return this .cosmosClientItem
519+ .getClient ()
497520 .getDatabase (this .config .getContainersConfig ().getDatabaseName ())
498521 .getContainer (containerProperties .getId ())
499522 .getFeedRanges ()
@@ -543,7 +566,10 @@ private String getClientMetadataCachesSnapshotString() {
543566 .map (CosmosContainerProperties ::getId )
544567 .collect (Collectors .toList ()))
545568 .block ();
546- CosmosAsyncDatabase database = this .cosmosClient .getDatabase (containersConfig .getDatabaseName ());
569+ CosmosAsyncDatabase database =
570+ this .cosmosClientItem
571+ .getClient ()
572+ .getDatabase (containersConfig .getDatabaseName ());
547573
548574 // read a random item from each container to populate the collection cache
549575 for (String containerName : containerNames ) {
@@ -556,7 +582,8 @@ private String getClientMetadataCachesSnapshotString() {
556582 if (cosmosThroughputControlConfig .isThroughputControlEnabled ()) {
557583 if (cosmosThroughputControlConfig .getThroughputControlAccountConfig () == null ) {
558584 CosmosAsyncContainer throughputControlContainer =
559- this .cosmosClient
585+ this .cosmosClientItem
586+ .getClient ()
560587 .getDatabase (cosmosThroughputControlConfig .getGlobalThroughputControlDatabaseName ())
561588 .getContainer (cosmosThroughputControlConfig .getGlobalThroughputControlContainerName ());
562589 readRandomItemFromContainer (throughputControlContainer );
@@ -568,33 +595,36 @@ private String getClientMetadataCachesSnapshotString() {
568595 readRandomItemFromContainer (database .getContainer (this .config .getMetadataConfig ().getStorageName ()));
569596 }
570597
571- return KafkaCosmosUtils .convertClientMetadataCacheSnapshotToString (this .cosmosClient );
598+ return KafkaCosmosUtils .convertClientMetadataCacheSnapshotToString (this .cosmosClientItem . getClient () );
572599 }
573600
574601 private String getThroughputControlClientMetadataCachesSnapshotString () {
575- CosmosAsyncClient throughputControlClient = null ;
602+ CosmosClientCacheItem throughputControlClientItem = null ;
576603 try {
577604 CosmosThroughputControlConfig throughputControlConfig = this .config .getThroughputControlConfig ();
578605 if (throughputControlConfig .isThroughputControlEnabled ()
579606 && throughputControlConfig .getThroughputControlAccountConfig () != null ) {
580- throughputControlClient = CosmosClientStore .getCosmosClient (
607+ throughputControlClientItem = CosmosClientCache .getCosmosClient (
581608 this .config .getThroughputControlConfig ().getThroughputControlAccountConfig (),
582609 this .connectorName
583610 );
584611 }
585612
586- if (throughputControlClient != null ) {
613+ if (throughputControlClientItem != null ) {
587614 this .readRandomItemFromContainer (
588- throughputControlClient
615+ throughputControlClientItem
616+ .getClient ()
589617 .getDatabase (throughputControlConfig .getGlobalThroughputControlDatabaseName ())
590618 .getContainer (throughputControlConfig .getGlobalThroughputControlContainerName ())
591619 );
620+ return KafkaCosmosUtils .convertClientMetadataCacheSnapshotToString (throughputControlClientItem .getClient ());
592621 }
593622
594- return KafkaCosmosUtils .convertClientMetadataCacheSnapshotToString (throughputControlClient );
623+ return null ;
624+
595625 } finally {
596- if (throughputControlClient != null ) {
597- throughputControlClient . close ( );
626+ if (throughputControlClientItem != null ) {
627+ CosmosClientCache . releaseCosmosClient ( throughputControlClientItem . getClientConfig () );
598628 }
599629 }
600630 }
0 commit comments