1
- /**
1
+ /*
2
2
* The MIT License (MIT)
3
3
* Copyright (c) 2018 Microsoft Corporation
4
4
*
23
23
24
24
package com .microsoft .azure .cosmosdb .sample ;
25
25
26
- import java .io .IOException ;
27
- import java .util .ArrayList ;
28
- import java .util .List ;
29
- import java .util .concurrent .CountDownLatch ;
30
-
31
26
import com .microsoft .azure .cosmosdb .ConnectionPolicy ;
32
27
import com .microsoft .azure .cosmosdb .ConsistencyLevel ;
33
28
import com .microsoft .azure .cosmosdb .Database ;
42
37
import com .microsoft .azure .cosmosdb .SqlParameterCollection ;
43
38
import com .microsoft .azure .cosmosdb .SqlQuerySpec ;
44
39
import com .microsoft .azure .cosmosdb .rx .AsyncDocumentClient ;
45
-
46
40
import rx .Observable ;
41
+ import rx .Scheduler ;
42
+ import rx .schedulers .Schedulers ;
43
+
44
+ import java .io .IOException ;
45
+ import java .util .ArrayList ;
46
+ import java .util .List ;
47
+ import java .util .concurrent .CountDownLatch ;
48
+ import java .util .concurrent .ExecutorService ;
49
+ import java .util .concurrent .Executors ;
47
50
48
51
public class Main {
52
+ private final ExecutorService executorService ;
53
+ private final Scheduler scheduler ;
49
54
50
55
private AsyncDocumentClient client ;
51
56
52
- private String databaseName = "AzureSampleFamilyDB" ;
53
- private String collectionName = "FamilyCollection" ;
57
+ private final String databaseName = "AzureSampleFamilyDB" ;
58
+ private final String collectionName = "FamilyCollection" ;
59
+
60
+ public Main () {
61
+ executorService = Executors .newFixedThreadPool (100 );
62
+ // The SDK uses netty library for doing async IO operations, and the operations are performed the netty io threads.
63
+ // The number of IO netty threads are limited; it is the same as the number of CPU cores.
64
+
65
+ // The app should avoid doing anything which takes a lot of time from IO netty thread.
66
+ // If the app consumes too much of IO netty thread you may face:
67
+ // * low throughput
68
+ // * bad latency
69
+ // * ReadTimeoutException because there is no netty IO thread available to read data from network.
70
+ // * deadlock
71
+
72
+ // The app code will receive the data from Azure Cosmos DB on the netty IO thread.
73
+ // The app should ensure the user's blocking call or computationally/IO heavy work after receiving data
74
+ // from Azure Cosmos DB is performed on a custom thread managed by the user (not the SDK netty IO threads).
75
+ //
76
+ // If you are doing blocking calls or heavy work, provide your own scheduler to switch thread.
77
+ // for example you can do this:
78
+ // client.createDocument(.).observeOn(userCustomScheduler).toBlocking().single();
79
+
80
+ // the following scheduler is used for switching from netty thread to user app thread.
81
+ scheduler = Schedulers .from (executorService );
82
+ }
83
+
84
+ public void close () {
85
+ executorService .shutdown ();
86
+ client .close ();
87
+ }
54
88
55
89
/**
56
90
* Run a Hello DocumentDB console application.
57
- *
58
- * @param args
91
+ *
92
+ * @param args command line args.
59
93
*/
60
94
public static void main (String [] args ) {
61
95
62
96
Main p = new Main ();
63
97
64
- try {
98
+ try {
65
99
p .getStartedDemo ();
66
100
System .out .println (String .format ("Demo complete, please hold while resources are deleted" ));
67
101
} catch (Exception e ) {
68
- System .out .println (String .format ("DocumentDB GetStarted failed with %s" , e ));
102
+ System .err .println (String .format ("DocumentDB GetStarted failed with %s" , e ));
69
103
} finally {
70
104
System .out .println ("close the client" );
71
- p .client .close ();
72
- System .exit (0 );
105
+ p .close ();
73
106
}
74
107
}
75
108
@@ -78,13 +111,13 @@ private void getStartedDemo() throws Exception {
78
111
79
112
client = new AsyncDocumentClient .Builder ()
80
113
.withServiceEndpoint (AccountSettings .HOST )
81
- .withMasterKey (AccountSettings .MASTER_KEY )
114
+ .withMasterKeyOrResourceToken (AccountSettings .MASTER_KEY )
82
115
.withConnectionPolicy (ConnectionPolicy .GetDefault ())
83
116
.withConsistencyLevel (ConsistencyLevel .Session )
84
117
.build ();
85
118
86
- this . createDatabaseIfNotExists ();
87
- this . createDocumentCollectionIfNotExists ();
119
+ createDatabaseIfNotExists ();
120
+ createDocumentCollectionIfNotExists ();
88
121
89
122
Family andersenFamily = Families .getAndersenFamilyDocument ();
90
123
Family wakefieldFamily = Families .getWakefieldFamilyDocument ();
@@ -126,42 +159,44 @@ private void createDatabaseIfNotExists() throws Exception {
126
159
127
160
String databaseLink = String .format ("/dbs/%s" , databaseName );
128
161
129
- Observable <ResourceResponse <Database >> databaseReadObs =
162
+ Observable <ResourceResponse <Database >> databaseReadObs =
130
163
client .readDatabase (databaseLink , null );
131
164
132
- Observable <ResourceResponse <Database >> databaseExistenceObs =
165
+ Observable <ResourceResponse <Database >> databaseExistenceObs =
133
166
databaseReadObs
134
- .doOnNext (x -> {
135
- System .out .println ("database " + databaseName + " already exists." );
136
- })
137
- .onErrorResumeNext (
138
- e -> {
139
- // if the database doesn't already exists
140
- // readDatabase() will result in 404 error
141
- if (e instanceof DocumentClientException ) {
142
- DocumentClientException de = (DocumentClientException ) e ;
143
- // if database
144
- if (de .getStatusCode () == 404 ) {
145
- // if the database doesn't exist, create it.
146
- System .out .println ("database " + databaseName + " doesn't existed,"
147
- + " creating it..." );
148
-
149
- Database dbDefinition = new Database ();
150
- dbDefinition .setId (databaseName );
151
-
152
- return client .createDatabase (dbDefinition , null );
153
- }
154
- }
155
-
156
- // some unexpected failure in reading database happened.
157
- // pass the error up.
158
- System .err .println ("Reading database " + databaseName + " failed." );
159
- return Observable .error (e );
160
- });
161
-
162
-
163
- // wait for completion
164
- databaseExistenceObs .toCompletable ().await ();
167
+ .doOnNext (x -> {
168
+ System .out .println ("database " + databaseName + " already exists." );
169
+ })
170
+ .onErrorResumeNext (
171
+ e -> {
172
+ // if the database doesn't already exists
173
+ // readDatabase() will result in 404 error
174
+ if (e instanceof DocumentClientException ) {
175
+ DocumentClientException de = (DocumentClientException ) e ;
176
+ // if database
177
+ if (de .getStatusCode () == 404 ) {
178
+ // if the database doesn't exist, create it.
179
+ System .out .println ("database " + databaseName + " doesn't existed,"
180
+ + " creating it..." );
181
+
182
+ Database dbDefinition = new Database ();
183
+ dbDefinition .setId (databaseName );
184
+
185
+ return client .createDatabase (dbDefinition , null );
186
+ }
187
+ }
188
+
189
+ // some unexpected failure in reading database happened.
190
+ // pass the error up.
191
+ System .err .println ("Reading database " + databaseName + " failed." );
192
+ return Observable .error (e );
193
+ });
194
+
195
+
196
+ // wait for completion,
197
+ // as waiting for completion is a blocking call try to
198
+ // provide your own scheduler to avoid stealing netty io threads.
199
+ databaseExistenceObs .toCompletable ().observeOn (scheduler ).await ();
165
200
166
201
System .out .println ("Checking database " + databaseName + " completed!\n " );
167
202
}
@@ -176,76 +211,76 @@ private void createDocumentCollectionIfNotExists() throws Exception {
176
211
177
212
String databaseLink = String .format ("/dbs/%s" , databaseName );
178
213
179
- client .queryCollections (databaseLink ,
180
- new SqlQuerySpec ("SELECT * FROM r where r.id = @id" ,
214
+ client .queryCollections (databaseLink ,
215
+ new SqlQuerySpec ("SELECT * FROM r where r.id = @id" ,
181
216
new SqlParameterCollection (
182
217
new SqlParameter ("@id" , collectionName ))), null )
183
- .single () // we know there is only single page of result (empty or with a match)
184
- .flatMap (page -> {
185
- if (page .getResults ().isEmpty ()) {
186
- // if there is no matching collection create the collection.
187
- DocumentCollection collection = new DocumentCollection ();
188
- collection .setId (collectionName );
189
- System .out .println ("Creating collection " + collectionName );
190
-
191
- return client .createCollection (databaseLink , collection , null );
192
- } else {
193
- // collection already exists, nothing else to be done.
194
- System .out .println ("Collection " + collectionName + "already exists" );
195
- return Observable .empty ();
196
- }
197
- }).toCompletable ().await ();
218
+ .single () // we know there is only single page of result (empty or with a match)
219
+ .flatMap (page -> {
220
+ if (page .getResults ().isEmpty ()) {
221
+ // if there is no matching collection create the collection.
222
+ DocumentCollection collection = new DocumentCollection ();
223
+ collection .setId (collectionName );
224
+ System .out .println ("Creating collection " + collectionName );
225
+
226
+ return client .createCollection (databaseLink , collection , null );
227
+ } else {
228
+ // collection already exists, nothing else to be done.
229
+ System .out .println ("Collection " + collectionName + "already exists" );
230
+ return Observable .empty ();
231
+ }
232
+ }).toCompletable (). observeOn ( scheduler ).await ();
198
233
199
234
System .out .println ("Checking collection " + collectionName + " completed!\n " );
200
235
}
201
236
202
- private void createFamiliesAsyncAndRegisterListener (List <Family > families , CountDownLatch completionLatch ) throws Exception {
237
+ private void createFamiliesAsyncAndRegisterListener (List <Family > families , CountDownLatch completionLatch ) {
203
238
204
239
String collectionLink = String .format ("/dbs/%s/colls/%s" , databaseName , collectionName );
205
240
206
241
List <Observable <ResourceResponse <Document >>> createDocumentsOBs = new ArrayList <>();
207
- for (Family family : families ) {
242
+ for (Family family : families ) {
208
243
Observable <ResourceResponse <Document >> obs = client .createDocument (
209
244
collectionLink , family , new RequestOptions (), true );
210
245
createDocumentsOBs .add (obs );
211
246
}
212
247
213
248
Observable .merge (createDocumentsOBs )
214
- .map (ResourceResponse ::getRequestCharge )
215
- .reduce ((sum , value ) -> sum + value )
216
- .subscribe (
217
- totalRequestCharge -> {
218
- // this will get print out when completed
219
- System .out .println ("total charge for creating documents is "
220
- + totalRequestCharge );
221
- },
222
-
223
- // terminal error signal
224
- e -> {
225
- e .printStackTrace ();
226
- completionLatch .countDown ();
227
- },
249
+ .map (ResourceResponse ::getRequestCharge )
250
+ .reduce ((sum , value ) -> sum + value )
251
+ .subscribe (
252
+ totalRequestCharge -> {
253
+ // this will get print out when completed
254
+ System .out .println ("total charge for creating documents is "
255
+ + totalRequestCharge );
256
+ },
257
+
258
+ // terminal error signal
259
+ e -> {
260
+ e .printStackTrace ();
261
+ completionLatch .countDown ();
262
+ },
228
263
229
- // terminal completion signal
230
- () -> {
231
- completionLatch .countDown ();
232
- });
264
+ // terminal completion signal
265
+ () -> {
266
+ completionLatch .countDown ();
267
+ });
233
268
}
234
269
235
270
private void createFamiliesAndWaitForCompletion (List <Family > families ) throws Exception {
236
271
237
272
String collectionLink = String .format ("/dbs/%s/colls/%s" , databaseName , collectionName );
238
273
239
274
List <Observable <ResourceResponse <Document >>> createDocumentsOBs = new ArrayList <>();
240
- for (Family family : families ) {
275
+ for (Family family : families ) {
241
276
Observable <ResourceResponse <Document >> obs = client .createDocument (
242
277
collectionLink , family , new RequestOptions (), true );
243
278
createDocumentsOBs .add (obs );
244
279
}
245
280
246
281
Double totalRequestCharge = Observable .merge (createDocumentsOBs )
247
282
.map (ResourceResponse ::getRequestCharge )
248
- .reduce ((sum , value ) -> sum + value )
283
+ .reduce ((sum , value ) -> sum + value )
249
284
.toBlocking ().single ();
250
285
251
286
writeToConsoleAndPromptToContinue (String .format ("Created %d documents with total request charge of %.2f" ,
@@ -260,7 +295,7 @@ private void executeSimpleQueryAsyncAndRegisterListenerForResult(CountDownLatch
260
295
queryOptions .setEnableCrossPartitionQuery (true );
261
296
262
297
String collectionLink = String .format ("/dbs/%s/colls/%s" , databaseName , collectionName );
263
- Observable <FeedResponse <Document >> queryObservable =
298
+ Observable <FeedResponse <Document >> queryObservable =
264
299
client .queryDocuments (collectionLink ,
265
300
"SELECT * FROM Family WHERE Family.lastName = 'Andersen'" , queryOptions );
266
301
0 commit comments