NetApp Closes Acquisition of Instaclustr Read the announcement
  • Cadence
  • Apache Kafka
  • Technical
Spinning Your Drones With Cadence and Apache Kafka – Integration Patterns and New Cadence Features

In this blog, we continue with our Cadence Drone Delivery application – with a summary of the complete workflow and the Cadence+Kafka integration patterns. We’ll also take look at some of the new Cadence features we used (retries, continue as new, queries, side-effects), and reveal an example trace of a drone delivery.

1. Movement and Delivery

(Source: Shutterstock)

The details of Drone movement were explained in the previous blog, but to summarise, the Drone Workflow is responsible for “moving” from the base to the order location, pickup up the order, flying to the delivery location, dropping the delivery, and returning to base. This is done with the nextLeg() activity, and if it has picked up the order, but hasn’t delivered it yet, it sends location state change signals to the Order Workflow (6). An interesting extension could be to update an ETA in the Order Workflow. Once the drone returns to base it “checks” the delivery and signals the Order Workflow that it’s complete, resulting in the Order Workflow completing (7), and the Drone Workflow starting a new instance of itself (8).

2. Summary of Cadence+Kafka Integration Patterns

So now we’ve seen several examples of different ways that Cadence and Kafka can integrate, including some from our previous blog. We’ll summarize them in a table for ease of reference.

Pattern NumberPattern NameDirectionPurposeFeaturesExamples
1Send a message to KafkaCadence → KafkaSend a single message to Kafka topic, simple notification (no response)Wraps a Kafka Producer in a Cadence ActivityBlog 2, Blog 4
2Request/    response from Cadence to KafkaCadence → Kafka → CadenceReuse Kafka microservice from Cadence, request and response to/from KafkaWraps a Kafka Producer in a Cadence Activity to send message and header meta-data for reply; Kafka consumer uses meta-data to signal Cadence Workflow or Activity with result to continueBlog 2
3Start New Cadence Workflow from KafkaKafka → CadenceStart Cadence Workflow from Kafka Consumer in response to a new record being receivedKafka Consumer starts Cadence Workflow instance running, one instance per record receivedBlog 4
4Get the next job from a queueCadence → Kafka → CadenceEach workflow instance gets a single job from a Kafka topic to processCadence Activity wraps a Kafka Consumer which continually polls the Kafka topic, returns a single record only, transient for the duration of activity onlyBlog 4

3. New Cadence Features Used

In the Drone Delivery Demo application, we’ve snuck in a few new Cadence features, so I’ll complete this blog by highlighting them.

3.1 Cadence Retries

(Source: Shutterstock)

Cadence Activities and Workflows can fail for a variety of reasons, including timeouts. You can either handle retries manually or get the Cadence server to retry for you, which is easier. The Cadence documentation on retries is here and here. For my Drone Delivery application, I decided to use retries for my activities. This is because they are potentially “long” running, and if they fail they can be resumed and continue from where they left off. The waitForOrder() activity is a blocking activity that only returns when it has acquired an Order that needs delivery, so it can potentially take an indefinite period of time. It’s also idempotent, so calling it multiple times works fine (as we saw above, it’s actually a Kafka consumer polling a topic). And the nextLeg()activity is responsible for plotting the Drone’s progress from one location to another, so can take many minutes to complete. If it fails, we want it to resume, but from the current location (the latest version now uses a query method to get the current drone location at the start of the activity). The easiest way to set the retry options is with a @MethodRetry annotation in the Activities interface like this example. Getting the settings including the times right may be challenging, however, and I’ve only “guessed” these:

  • maximumAttempts is the maximum number of retries permitted before actual failure 
  • initialIntervalSeconds is the delay until first retry
  • expirationSeconds is the maximum number of seconds for all the retry attempts.
    • Retries stop whenever maximumAttempts or expirationSeconds is reached (and only one is required)
  • maximumIntervalSeconds is the cap of the interval between retries, an exponential backoff strategy is used and this caps the interval rather than letting it increase too much

3.2 Cadence Continue As New

You may be tempted to keep a Cadence workflow running forever—after all, they are designed for long-running processes. However, this is generally regarded as a “bad thing” as the size of the workflow state will keep on increasing, possibly exceeding the maximum state size, and slowing down recomputing the current state of workflows from the state history. The simple solution that I used is to start a new Drone workflow at the end of the existing workflow, using Workflow.continueAsNew(), which starts a new workflow instance with the same workflow ID, but none of the states, as the original. This means that Drone Workflows start at the base, are fully charged, and don’t have an Order to pick up yet.

3.3 Cadence Queries

In the 2nd Cadence blog, we discovered that workflows must define an interface class, and the interface methods can have annotations including @WorkflowMethod (the entry point to a workflow, exactly one method must have this), and @SignalMethod (a method that reacts to external signals, zero or more methods can have this annotation). 

However, there’s another annotation that we didn’t use, @QueryMethod. This annotation indicates a method that reacts to synchronous query requests, and you can have lots of them. They are designed to expose state to other workflows, etc., so they are basically a “getter” method.  For example, here’s the full list of methods, including some query methods, for the OrderWorkflow:

Queries must be read-only and non-blocking. One major difference between signals and queries that I noticed is that signals only work on non-completed workflows, but queries work on both non-completed and completed workflows. For completed workflows, the workflows are automatically restarted so that their end state can be returned. Clever!

3.4 Cadence Side Effects

Cadence Workflow code must in general be deterministic

(Source: Shutterstock)

A final trick that I used for this application was due to my use of random numbers to compute Order and Delivery locations, in the newDestination() function. In order to use this correctly in the Order Activities Workflow I had to tell Cadence that it has side effects using the Workflow.sideEffect() method. This allows workflows to execute the provided function once and records the result into the workflow history. The recorded history result will be returned without executing the provided function during replay. This guarantees the deterministic requirement for workflows, as the exact same result will be returned in the replay. Here’s my example from the startWorkflow() method of the Order workflow (above):

The code is available in our Github repository. In the next blog in the series we’ll explore Cadence scalability and see how many Drones we can get flying at once.

Appendix: Example Trace

If all goes according to plan a typical (abbreviated) Drone and Order Workflow Trace looks like this:

To learn more about Cadence and the benefits it can provide your organisation

Download white paper

Follow the series: Spinning Your Drones With Cadence

Part 1: Spinning Your Workflows With Cadence!

Part 2: Spinning Apache Kafka® Microservices With Cadence Workflows

Part 3: Spinning your drones with Cadence

Part 4: Architecture, Order and Delivery Workflows

Part 5 Integration Patterns and New Cadence Features

Other articles
Read All