Skip to content

Commit b90a1d4

Browse files
authored
Merge pull request #109900 from anfeldma-ms/CosmosDBJavaE2ESampleQuickstart
Added end-to-end sample tutorial
2 parents 02092fd + 97fa2c6 commit b90a1d4

13 files changed

+255
-1
lines changed

articles/cosmos-db/TOC.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
href: sql-api-java-get-started.md
4040
- name: Async Java
4141
href: sql-api-async-java-get-started.md
42+
- name: Async Java end-to-end sample app
43+
href: create-sql-api-java-changefeed.md
4244
- name: Node.js
4345
href: sql-api-nodejs-get-started.md
4446
- name: Build a web app
@@ -729,7 +731,7 @@
729731
items:
730732
- name: Overview
731733
items:
732-
- name: What is Germlin API in Cosmos DB?
734+
- name: What is Gremlin API in Cosmos DB?
733735
href: graph-introduction.md
734736
- name: Wire protocol support
735737
href: gremlin-support.md
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
---
2+
title: Tutorial - an end-to-end Async Java SQL API application sample with Change Feed
3+
description: This tutorial walks you through a simple Java SQL API application which inserts documents into an Azure Cosmos DB container, while maintaining a materialized view of the container using Change Feed.
4+
author: anfeldma
5+
ms.service: cosmos-db
6+
ms.subservice: cosmosdb-sql
7+
ms.devlang: java
8+
ms.topic: tutorial
9+
ms.date: 04/01/2020
10+
ms.author: anfeldma
11+
---
12+
13+
# Tutorial - an end-to-end Async Java SQL API application sample with Change Feed
14+
15+
This tutorial guide walks you through a simple Java SQL API application which inserts documents into an Azure Cosmos DB container, while maintaining a materialized view of the container using Change Feed.
16+
17+
## Prerequisites
18+
19+
* Personal computer
20+
21+
* The URI and key for your Azure Cosmos DB account
22+
23+
* Maven
24+
25+
* Java 8
26+
27+
## Background
28+
29+
The Azure Cosmos DB Change Feed provides an event-driven interface to trigger actions in response to document insertion. This has many uses. For example in applications which are both read and write heavy, a chief use of Change Feed is to create a real-time **materialized view** of a container as it is ingesting documents. The materialized view container will hold the same data but partitioned for efficient reads, making the application both read and write efficient.
30+
31+
The work of managing Change Feed events is largely taken care of by the Change Feed Processor library built into the SDK. This library is powerful enough to distribute Change Feed events among multiple workers, if that is desired. All you have to do is provide the Change Feed library a callback.
32+
33+
This simple example demonstrates Change Feed Processor library with a single worker creating and deleting documents from a materialized view.
34+
35+
## Setup
36+
37+
If you have not already done so, clone the app example repo:
38+
39+
```bash
40+
git clone https://github.com/Azure-Samples/azure-cosmos-java-sql-app-example.git
41+
```
42+
43+
> You have a choice to work through this Quickstart with Java SDK 4.0 or Java SDK 3.7.0. **If you would like to use Java SDK 3.7.0, in the terminal type ```git checkout SDK3.7.0```**. Otherwise, stay on the ```master``` branch, which defaults to Java SDK 4.0.
44+
45+
Open a terminal in the repo directory. Build the app by running
46+
47+
```bash
48+
mvn clean package
49+
```
50+
51+
## Walkthrough
52+
53+
1. As a first check, you should have an Azure Cosmos DB account. Open the **Azure Portal** in your browser, go to your Azure Cosmos DB account, and in the left pane navigate to **Data Explorer**.
54+
55+
![Azure Cosmos DB account](media/create-sql-api-java-changefeed/cosmos_account_empty.JPG)
56+
57+
1. Run the app in the terminal using the following command:
58+
59+
```bash
60+
mvn exec:java -Dexec.mainClass="com.azure.cosmos.workedappexample.SampleGroceryStore" -DACCOUNT_HOST="your-account-uri" -DACCOUNT_KEY="your-account-key" -Dexec.cleanupDaemonThreads=false
61+
```
62+
63+
1. Press enter when you see
64+
65+
```bash
66+
Press enter to create the grocery store inventory system...
67+
```
68+
69+
then return to the Azure Portal Data Explorer in your browser. You will see a database **GroceryStoreDatabase** has been added with three empty containers:
70+
71+
* **InventoryContainer** - The inventory record for our example grocery store, partitioned on item ```id``` which is a UUID.
72+
* **InventoryContainer-pktype** - A materialized view of the inventory record, optimized for queries over item ```type```
73+
* **InventoryContainer-leases** - A leases container is always needed for Change Feed; leases track the app's progress in reading the Change Feed.
74+
75+
76+
![Empty containers](media/create-sql-api-java-changefeed/cosmos_account_resources_lease_empty.JPG)
77+
78+
79+
1. In the terminal, you should now see a prompt
80+
81+
```bash
82+
Press enter to start creating the materialized view...
83+
```
84+
85+
Press enter. Now the following block of code will execute and initialize the Change Feed processor on another thread:
86+
87+
88+
**Java SDK 4.0**
89+
```java
90+
changeFeedProcessorInstance = getChangeFeedProcessor("SampleHost_1", feedContainer, leaseContainer);
91+
changeFeedProcessorInstance.start()
92+
.subscribeOn(Schedulers.elastic())
93+
.doOnSuccess(aVoid -> {
94+
isProcessorRunning.set(true);
95+
})
96+
.subscribe();
97+
98+
while (!isProcessorRunning.get()); //Wait for Change Feed processor start
99+
```
100+
101+
**Java SDK 3.7.0**
102+
```java
103+
changeFeedProcessorInstance = getChangeFeedProcessor("SampleHost_1", feedContainer, leaseContainer);
104+
changeFeedProcessorInstance.start()
105+
.subscribeOn(Schedulers.elastic())
106+
.doOnSuccess(aVoid -> {
107+
isProcessorRunning.set(true);
108+
})
109+
.subscribe();
110+
111+
while (!isProcessorRunning.get()); //Wait for Change Feed processor start
112+
```
113+
114+
```"SampleHost_1"``` is the name of the Change Feed processor worker. ```changeFeedProcessorInstance.start()``` is what actually starts the Change Feed processor.
115+
116+
Return to the Azure Portal Data Explorer in your browser. Under the **InventoryContainer-leases** container, click **items** to see its contents. You will see that Change Feed Processor has populated the lease container, i.e. the processor has assigned the ```SampleHost_1``` worker a lease on some partitions of the **InventoryContainer**.
117+
118+
![Leases](media/create-sql-api-java-changefeed/cosmos_leases.JPG)
119+
120+
1. Press enter again in the terminal. This will trigger 10 documents to be inserted into **InventoryContainer**. Each document insertion appears in the Change Feed as JSON; the following callback code handles these events by mirroring the JSON documents into a materialized view:
121+
122+
**Java SDK 4.0**
123+
```java
124+
public static ChangeFeedProcessor getChangeFeedProcessor(String hostName, CosmosAsyncContainer feedContainer, CosmosAsyncContainer leaseContainer) {
125+
ChangeFeedProcessorOptions cfOptions = new ChangeFeedProcessorOptions();
126+
cfOptions.setFeedPollDelay(Duration.ofMillis(100));
127+
cfOptions.setStartFromBeginning(true);
128+
return ChangeFeedProcessor.changeFeedProcessorBuilder()
129+
.setOptions(cfOptions)
130+
.setHostName(hostName)
131+
.setFeedContainer(feedContainer)
132+
.setLeaseContainer(leaseContainer)
133+
.setHandleChanges((List<JsonNode> docs) -> {
134+
for (JsonNode document : docs) {
135+
//Duplicate each document update from the feed container into the materialized view container
136+
updateInventoryTypeMaterializedView(document);
137+
}
138+
139+
})
140+
.build();
141+
}
142+
143+
private static void updateInventoryTypeMaterializedView(JsonNode document) {
144+
typeContainer.upsertItem(document).subscribe();
145+
}
146+
```
147+
148+
**Java SDK 3.7.0**
149+
```java
150+
public static ChangeFeedProcessor getChangeFeedProcessor(String hostName, CosmosContainer feedContainer, CosmosContainer leaseContainer) {
151+
ChangeFeedProcessorOptions cfOptions = new ChangeFeedProcessorOptions();
152+
cfOptions.feedPollDelay(Duration.ofMillis(100));
153+
cfOptions.startFromBeginning(true);
154+
return ChangeFeedProcessor.Builder()
155+
.options(cfOptions)
156+
.hostName(hostName)
157+
.feedContainer(feedContainer)
158+
.leaseContainer(leaseContainer)
159+
.handleChanges((List<CosmosItemProperties> docs) -> {
160+
for (CosmosItemProperties document : docs) {
161+
//Duplicate each document update from the feed container into the materialized view container
162+
updateInventoryTypeMaterializedView(document);
163+
}
164+
165+
})
166+
.build();
167+
}
168+
169+
private static void updateInventoryTypeMaterializedView(CosmosItemProperties document) {
170+
typeContainer.upsertItem(document).subscribe();
171+
}
172+
```
173+
174+
1. Allow the code to run 5-10sec. Then return to the Azure Portal Data Explorer and navigate to **InventoryContainer > items**. You should see that items are being inserted into the inventory container; note the partition key (```id```).
175+
176+
![Feed container](media/create-sql-api-java-changefeed/cosmos_items.JPG)
177+
178+
1. Now, in Data Explorer navigate to **InventoryContainer-pktype > items**. This is the materialized view - the items in this container mirror **InventoryContainer** because they were inserted programmatically by Change Feed. Note the partition key (```type```). So this materialized view is optimized for queries filtering over ```type```, which would be inefficient on **InventoryContainer** because it is partitioned on ```id```.
179+
180+
![Materialized view](media/create-sql-api-java-changefeed/cosmos_materializedview2.JPG)
181+
182+
1. We're going to delete a document from both **InventoryContainer** and **InventoryContainer-pktype** using just a single ```upsertItem()``` call. First, take a look at Azure Portal Data Explorer. We'll delete the document for which ```/type == "plums"```; it is encircled in red below
183+
184+
![Materialized view](media/create-sql-api-java-changefeed/cosmos_materializedview-emph-todelete.JPG)
185+
186+
Hit enter again to call the function ```deleteDocument()``` in the example code. This function, shown below, upserts a new version of the document with ```/ttl == 5```, which sets document Time-To-Live (TTL) to 5sec.
187+
188+
**Java SDK 4.0**
189+
```java
190+
public static void deleteDocument() {
191+
192+
String jsonString = "{\"id\" : \"" + idToDelete + "\""
193+
+ ","
194+
+ "\"brand\" : \"Jerry's\""
195+
+ ","
196+
+ "\"type\" : \"plums\""
197+
+ ","
198+
+ "\"quantity\" : \"50\""
199+
+ ","
200+
+ "\"ttl\" : 5"
201+
+ "}";
202+
203+
ObjectMapper mapper = new ObjectMapper();
204+
JsonNode document = null;
205+
206+
try {
207+
document = mapper.readTree(jsonString);
208+
} catch (Exception e) {
209+
e.printStackTrace();
210+
}
211+
212+
feedContainer.upsertItem(document,new CosmosItemRequestOptions()).block();
213+
}
214+
```
215+
216+
**Java SDK 3.7.0**
217+
```java
218+
public static void deleteDocument() {
219+
220+
String jsonString = "{\"id\" : \"" + idToDelete + "\""
221+
+ ","
222+
+ "\"brand\" : \"Jerry's\""
223+
+ ","
224+
+ "\"type\" : \"plums\""
225+
+ ","
226+
+ "\"quantity\" : \"50\""
227+
+ ","
228+
+ "\"ttl\" : 5"
229+
+ "}";
230+
231+
ObjectMapper mapper = new ObjectMapper();
232+
JsonNode document = null;
233+
234+
try {
235+
document = mapper.readTree(jsonString);
236+
} catch (Exception e) {
237+
e.printStackTrace();
238+
}
239+
240+
feedContainer.upsertItem(document,new CosmosItemRequestOptions()).block();
241+
}
242+
```
243+
244+
The Change Feed ```feedPollDelay``` is set to 100ms; therefore, Change Feed responds to this update almost instantly and calls ```updateInventoryTypeMaterializedView()``` shown above. That last function call will upsert the new document with TTL of 5sec into **InventoryContainer-pktype**.
245+
246+
The effect is that after about 5 seconds, the document will expire and be deleted from both containers.
247+
248+
This procedure is necessary because Change Feed only issues events on item insertion or update, not on item deletion.
249+
250+
1. Press enter one more time to close the program and clean up its resources.

articles/cosmos-db/index.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ landingContent:
176176
url: sql-api-dotnet-application.md
177177
- text: Java app with Async SDK
178178
url: sql-api-async-java-get-started.md
179+
- text: Java app sample with Change Feed
180+
url: create-sql-api-java-changefeed.md
179181
- text: Query data with SQL queries
180182
url: tutorial-query-sql-api.md
181183
- linkListType: how-to-guide
107 KB
Loading
74.4 KB
Loading
87.6 KB
Loading
92 KB
Loading
87.2 KB
Loading
137 KB
Loading
138 KB
Loading

0 commit comments

Comments
 (0)