BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles How Ousta Simulates Rides within a Two-Minute Test Cycle

How Ousta Simulates Rides within a Two-Minute Test Cycle

Bookmarks

Key takeaways

  • Ousta’s simulation service listens to the system events, and acts as a fleet of fake drivers through the driver application APIs
  • The event driven architecture follows the pub-sub model, with one publisher having one or more subscribers
  • Microservices represents individual testable and deployable units of the system, serving different functions
  • Loosely coupled systems are easier to test

Ousta is a leading ride hailing expansion in Egypt, with a fast growing customer base and a fleet of vehicles. Our system includes two mobile apps which interact with an event driven architecture using microservices. The combination of EDA and microservices facilitated a simulation system for automation. All of these combine to create an environment that allows us to move faster.

Problem Definition

We have two apps: one for riders and another for drivers. When we started implementing our application we decided to start with the customer-facing rider application which is used to request a ride.

Building an application with real-time updates comes with additional challenges. Every time we need to test a ride, the following high-level events need to be simulated:

  • A driver application is needed to accept the ride
  • Notification of arrival sent to the rider
  • Transport the rider to the destination
  • End the ride

Every time a change was introduced to the mobile application or the back-end, it was quite difficult to test the whole cycle, as we had nothing except the rider app and APIs.

Automation!

We started thinking of an automated solution to test the whole ride flow on the staging environment. How can we execute the driver APIs according to actions done by the rider application? We thought of an external microservice that is responsible for executing the driver APIs, listening to the system events raised due to rider actions. This was quite easy thanks to our event driven architecture.

Event Driven Architecture

In this way event driven architecture is powerful, as every action done to our system raises an event as a JSON object with all the information that could be needed.

For example, requesting a new trip raises an event with the rider details, pickup location, pricing, etc. Here’s a sample of the JSON event:

{  
// Meta part contains information about the event type and category.
  "meta":{  
      "messageCategory":"event_TripInfo", 
      "messageType":"newTrip"
   }, 
   "body":{  
      "tripId":"d2eac36c-8131-4409-8c37-863b0725fb4d", 
      "status":"processing", 
      "riderId":"4524b80a-f82a-4f1c-ad77-69a620c5c628", 
      "requestTime":"2016-09-09T15:42:32+0000", 
      "requestTimestamp":1473435752, 
      "currencyCode":"EGP", 
      "rider":{  
         "riderId":"4524b80a-f82a-4f1c-ad77-69a620c5c626", 
         "firstName":"Mostafa", 
         "lastName":"Saeed", 
         "emailAddress":"msaeedatteya@gmail.com", 
         "mobileNumber":"+201272509147", 
         "pictureUrl":null, 
         "rating":"5"
      }, 
      "service":{  
         "serviceId":"7f888b1f-14e4-4b7e-8f8d-1c1ee1df074c", 
         "displayName":"Super Saver", 
         "description":"Ousta super saver service", 
         "code":"Super Saver", 
        "image":"https:\/\/s3-eu-west-1.amazonaws.com\/oustaridersapp\/services\/ico_cartype_economy.png", 
     "carPictureUrl":"https:\/\/s3-eu-west-1.amazonaws.com\/oustaridersapp\/services\/img_onmap_car_taxi.png", 
         "priceDetails":{  
            "costPerDistance":"1.45", 
            "costPerMinute":"0.25", 
            "distanceUnit":"KM", 
            "base":"4.0", 
            "cancellation":"10", 
            "minimum":"10", 
            "currencyCode":"EGP", 
            "commissionPercentage":"0.2", 
            "commissionFees":"0", 
            "commissionType":"percentage"
         }
      }, 
      "city":{  
         "name":"Cairo", 
         "code":"CA", 
         "location":{  
            "latitude":"30.0594699", 
            "longitude":"31.1884239"
         }
      }, 
      "pickupLocation":{  
         "latitude":"30.067615031955", 
         "longitude":"31.210990101099", 
         "text":"Tahrir Square, Meret Basha, Qasr an Nile, Cairo Governorate"
      }, 
// Driver & vehicle are null because ride is not accepted by a driver yet.
      "driver": null, 
      "vehicle": null, 
     "paymentInfo":{  
         "paymentMethodId":"4496839d-8cd2-4f98-98bb-e059ca7ac5c1", 
         "type":"cash", 
      }
   }
}

This event can be handled by multiple other microservices such as sending push notifications, sms, email or maybe saving the rides details anywhere else for analytical purposes. We use Amazon SNS for publishing our events, and Amazon SQS for receiving and persisting events in the message queue for consumers.

  1. Using Amazon SNS (Simple Notification Service): AWS SNS is a publish subscribe service offered by Amazon used to creating topics that are related to your system logic, then publish events to these topics when a specific event occurs using AWS SDK; for example, when a trip event occurs, microservice responsible for reserving rides create the event JSON Object and publishes an event to TripEvents SNS Topic.

  2. Using Amazon SQS (Simple Queueing Service): AWS SQS is a message queueing service that is used to subscribe to a specific SNS Topic so that any message published to that topic persists in the message queue till being consumed by the service.

The following diagram shows how events flow through the system from being published until being handled by different microservices.

As shown in the diagram, Event Driven Architecture allows new microservices to be added easily to the system for different purposes only by following these steps:

  1. Adding a new SQS Queue
  2. Subscribing Queue to a specific SNS Topic
  3. Adding a new microservice that listens to that queue

The Simulator Microservice

We started implementing the simulator microservice. By listening to our TripEvents SNS Topic, it was quite easy taking an action according to the raised event. For example, when receiving the event indicating that a specific driver should accept or reject a ride request, the service makes an API call to accept the trip on behalf of the driver application as shown in the previous diagram.

At first we implemented a simple version, starting from a ride request till the end of ride flow. Calls executed one after another, with 10 seconds between each call. The whole ride workflow is as follows:

Fake Drivers All around Ousta HQ

We needed to make the simulation more realistic, so we started optimizing our simulator by simulating the drivers’ movement in the streets:

  • Before being requested for a ride
  • Going to the pickup location
  • Reaching the rider’s destination

So we came up with the idea of simulating drivers on our staging environment as if they were moving around our HQ in downtown, Cairo. We needed an API call for each driver to be executed every x seconds, informing the backend of the current location’s latitude & longitude.

Simulating The Driver Movement

As we needed the simulation to appear more realistic, we optimized the simulation service to get the real path in the form of a sequence of latitude and longitude points from the driver’s current location at the time the trip was accepted to the pick-up location, and from the pick-up location to the trip destination entered by the rider (requester) in the application. In order to achieve this we used Google Distance Matrix API to get the full route between two points in the form of a list of latitude and longitude points, so that the simulator can send these points sequentially as if the driver is moving from point A towards point B.

Location Update Trigger

The tricky part was how to set a scheduled trigger to send location updates every x seconds for every currently simulated driver. We thought of a solution using a cron job that can automatically trigger the service, but we faced a limitation of the minimum interval being one minute. So we needed to use Amazon SQS in a special way. SQS has a powerful feature called Delay Queues, so that a message added to the queue can be invisible x seconds before being consumed. The following diagram shows how we used this useful feature to handle the movement simulation.

Location Updates Sampling Rate

We chose the sampling rate to be every four seconds according to our application needs. Accurate and frequent location updates are critical to our application and rider experience because we rely on these updates to calculate distance covered by the driver during the trip.

Simulator Flow Summary

Testing Applications Independently

The simulation service allowed us to build the rider application with full features before we even had our driver application built, which solved a big dependency problem in our process so that we don’t have to use both applications to test the whole ride cycle.

Full Testing Cycle In Less than Two Minutes

With all the above effort done, we had a fully automated testing solution that allows us to release new features, apply changes, do Hot Fixes quickly with less fear, and make multiple production deployments daily.

About the Author

Mostafa Saeed is software architect at Ousta,a fast growing ride hailing startup in Egypt. Mostafa has lead the development team to achieve an event driven,fault tolerant & microservices based architecture. Also he is an AWS consultant who helps startups apply cloud high availability and scalability best practices.

Rate this Article

Adoption
Style

BT