Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Java food ordering example #62

Merged
merged 32 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a5cd66d
Java food ordering init
gvdongen Jan 3, 2024
cbbcbfb
Food ordering Java working setup
gvdongen Jan 3, 2024
0baf625
Cleanup
gvdongen Jan 3, 2024
f885748
Formatting
gvdongen Jan 3, 2024
dd74340
Cleanup
gvdongen Jan 3, 2024
16b2b38
Cleanup
gvdongen Jan 3, 2024
aa8c694
Cleanup and extra commenting
gvdongen Jan 4, 2024
bc3f1d4
Rename services, add demo scenario description, cleanup
gvdongen Jan 4, 2024
fc57974
Fix psql statement in readme
gvdongen Jan 4, 2024
c9635b0
Cleanup
gvdongen Jan 4, 2024
9c3c29c
Reduce log level to info
gvdongen Jan 4, 2024
09bfed1
Update some comments
gvdongen Jan 11, 2024
e5a234a
Address review feedback
gvdongen Jan 12, 2024
e0d00a8
Update java/food-ordering/app/restate-app/src/main/java/dev/restate/s…
gvdongen Jan 12, 2024
b6f8430
Update java/food-ordering/app/restate-app/src/main/java/dev/restate/s…
gvdongen Jan 12, 2024
3b33897
Update java/food-ordering/app/restate-app/src/main/java/dev/restate/s…
gvdongen Jan 12, 2024
bd57a72
Update java/food-ordering/app/restate-app/src/main/java/dev/restate/s…
gvdongen Jan 12, 2024
6113849
Update java/food-ordering/app/restate-app/src/main/java/dev/restate/s…
gvdongen Jan 12, 2024
6cefd13
Update java/food-ordering/app/restate-app/src/main/java/dev/restate/s…
gvdongen Jan 12, 2024
2c89154
Update java/food-ordering/app/restate-app/src/main/java/dev/restate/s…
gvdongen Jan 12, 2024
d37f7b8
Update java/food-ordering/app/restate-app/src/main/java/dev/restate/s…
gvdongen Jan 12, 2024
67208a1
Update java/food-ordering/app/restate-app/src/main/java/dev/restate/s…
gvdongen Jan 12, 2024
af65436
Address feedback
gvdongen Jan 12, 2024
1aa6e00
Add food ordering to zip creation
gvdongen Jan 12, 2024
7f7ed96
Update README
gvdongen Jan 12, 2024
324b7d0
Upgrade to 0.7.0 and use /deployments for registration
gvdongen Jan 15, 2024
b778695
Remove datafusion port
gvdongen Jan 15, 2024
e6a300a
Point gradle check workflow to correct directory
gvdongen Jan 16, 2024
c2bd59e
Fix paths to food order processing app in github workflows
gvdongen Jan 16, 2024
1d4d4f0
Add gradle wrapper jar
gvdongen Jan 16, 2024
ad6475e
Remove spotless
gvdongen Jan 16, 2024
a7259a6
Rename webui to avoid clashes with Typescript example
gvdongen Jan 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions java/food-ordering/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build

.idea
*.iml

# Unignore the gradle wrapper
!gradle/wrapper/gradle-wrapper.jar
100 changes: 100 additions & 0 deletions java/food-ordering/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Food ordering app with Restate

This example application implements an order processing middleware which sits between third-party food ordering providers and restaurants.
Food ordering providers interact with the application by publishing events to Kafka.
For each event, Restate triggers the order workflow.
The order workflow interacts with the restaurants' external point of sales system to request the preparation of the orders.
It also interacts with the delivery services to get the order delivered to the customer once preparation is done.

![demo_overview.png](demo_overview.png)


## Running locally with Docker compose

Build the docker containers:

```shell
cd app
./gradlew clean build jibDockerBuild
```

Launch the Docker compose setup:
```shell
docker compose up
```

WebUI is running at http://localhost:3000

Jaeger is running at http://localhost:16686

When you are making changes to the code, and you want to trigger a build of the Docker images:

```shell
docker compose build --no-cache
```

Clean up after bringing setup down:
```shell
docker compose rm
```

### Inspecting state and ongoing invocations

Restate has a psql interface to query the state of the system.

If you buy some products via the webUI, you can see how the order workflow is executed by querying the state of the order status service:
```shell
watch -n 1 'psql -h localhost -p 9071 -c "select service, service_key_utf8, key, value_utf8 from state s where s.service='"'"'order.OrderStatusService'"'"';"'
```

Or have a look at the state of all the services, except for the driver simulator:
```shell
watch -n 1 'psql -h localhost -p 9071 -c "select service, service_key_utf8, key, value_utf8 from state s where s.service not in ('"'"'order.DriverMobileAppSimulator'"'"');"'
```

Or you can check the state of the ongoing invocations via:
```shell
watch -n 1 'psql -h localhost -p 9071 -c "select service, method, service_key_utf8, id, status, invoked_by_service, invoked_by_id from sys_status;"'
```

## Exploring the demo

### The order workflow
You can find the implementation of each of the services under `app/restate-app/src/main/java/dev/restate/sdk/examples/`.
The flow of an incoming order is as follows:
1. When the customer places an order via the web UI (localhost:3000), an order event is published to Kafka.
2. Restate subscribes to the order topic and triggers the order workflow for each incoming event. This subscription is set up by executing two curl commands, as done in the Docker compose file (`docker-compose.yaml`) by the `runtimesetup` container.
3. The order workflow is implemented in `OrderWorkflow.java` and consists of the following steps:
1. When the order workflow is triggered, it first parses the raw Kafka event and extracts the order details.
2. It then calls the `OrderStatusService` to create a new order in the system. The `OrderStatusService` is a keyed service which tracks the status of each order by storing it in Restate's key-value store.
3. The order workflow then triggers the payment by calling a third-party payment provider (implemented as a stub in this example). To do this, the order workflow first generates an idempotency token via a side effect, and then uses this to call the payment provider. The payment provider can deduplicate retries via the idempotency key.
4. The workflow then sets the order status to `SCHEDULED` and sets a timer to continue processing after the delivery delay has passed. For example, if a customer ordered food for later in the day, the order will be scheduled for preparation at the requested time. If any failures occur during the sleep, Restate makes sure that the workflow will still wake up on time.
5. Once the timer fires, the order workflow creates an awakeable and sends a request to the restaurant point-of-sales system to start the preparation. This is done via an HTTP request from within a side effect. The status of the order is set to `IN_PREPARATION`. The restaurant will use the awakeable callback to signal when the prepration is done. Once this happens, the order workflow will continue and set the order status to `SCHEDULING_DELIVERY`.
6. Finally, the order workflow calls the delivery manager (`DeliveryManager.java`) to schedule the delivery of the order (see description below). It does this by using an awakeable, that the delivery manager will use to signal when the delivery is done. Once the delivery is done, the order workflow will set the order status to `DELIVERED`.

### The delivery workflow
To get the order delivered a set of services work together. The delivery manager (`start` method in `DeliveryManager.java`) implements the delivery workflow. It tracks the delivery status, by storing it in Restate's state store, and then requests a driver to do the delivery. To do that, it requests a driver from the DriverDeliveryMatcher. The DriverDeliveryMatcher tracks available drivers and pending deliveries for each region, and matches drivers to deliveries.
Once a driver has been found, the delivery manager assigns the delivery to the driver and sets the order status to `WAITING_FOR_DRIVER`. The delivery has started now. The delivery manager relies for the rest of the delivery updates on the driver digital twin.

The driver's digital twin (`DriverDigitalTwin.java`) is the digital representation of a driver in the field. Each driver has a mobile app on his phone (here simulated by `external/DriverMobileAppSimulator.java`) which continuously sends updates to the digital twin of the driver:
1. The driver can notify when they start working: have a look at `DriverMobileAppSimulator/StartDriver` which calls `DriverDigitalTwin/SetDriverAvailable`.
2. The mobile app also polls the digital twin to check if a new delivery was assigned to the driver. Have a look at `DriverMobileAppSimulator/PollForWork` which regularly calls `DriverDigitalTwin/GetAssignedDelivery`.
3. During delivery, the mobile app sends regular location updates over Kafka to the digital twin of the driver. Have a look at the method `DriverDigitalTwin/HandleDriverLocationUpdateEvent`.
4. Once the driver has arrived at the restaurant, the driver's mobile app notifies its digital twin (by calling `DriverDigitalTwin/NotifyDeliveryPickup`). The digital twin then notifies the delivery manager that the driver has picked up the delivery (by calling `DeliveryManager/NotifyDeliveryPickup`).
5. Finally, the driver arrives at the customer and the driver's mobile app notifies its digital twin (by calling `DriverDigitalTwin/NotifyDeliveryDelivered`). The digital twin then notifies the delivery manager that the driver has picked up the delivery (by calling `DeliveryManager/NotifyDeliveryDelivered`).
6. The delivery manager then sets the order status to `DELIVERED`. And the order workflow gets completed, by resolving the awakeable.


## Attribution

The implementation of the web app is based on the MIT Licensed repository here: https://github.com/jeffersonRibeiro/react-shopping-cart.

## Releasing (for Restate developers)

### Upgrading Java SDK
Upgrade the Restate SDK dependencies for the `app`.
Then run the example via Docker compose.

### Upgrading Restate runtime
The Docker Compose setup uses the latest Restate runtime version.
Test run the example via Docker compose.
26 changes: 26 additions & 0 deletions java/food-ordering/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
plugins {
java
application

id("com.diffplug.spotless").version("6.6.1")
}

repositories {
mavenCentral()
}

// Configure test platform
tasks.withType<Test> {
useJUnitPlatform()
}


allprojects {
apply(plugin = "com.diffplug.spotless")
configure<com.diffplug.gradle.spotless.SpotlessExtension> {
java {
googleJavaFormat()
targetExclude("build/generated/**/*.java")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading