Coroutines and Kotlin: What this Means to Mobility
I’m a Software Engineer for our Driver Operations Tribe in our Barcelona Tech Hub, where we focus on the FREE NOW Driver tools. Myself and the team strive to ensure that all of our drivers have all tools that are innovative, accurate and above all, a tool that they can rely on during their day to day work in whichever city they are working in. Of course, selecting and developing high-quality processes and software is key for the teams in Barcelona, so I shed some light on why FREE NOW decided to start using Coroutines to innovate the Driver Operations experience.
What is Coroutines?
Coroutines are one of the main Kotlin features now. They are lightweight threads. Coroutines can work in parallel, wait for other Coroutines and notify each other like threads. The biggest difference between Coroutines and threads is resource consumption. A huge advantage of Coroutines is that it is unbelievably cheap for the application processes. The application can create thousands of Coroutines without impacting performance. At the same time, threads are expensive and a thousand threads can be a problem even for a very powerful machine.
We decided to use Coroutines because they are easy to implement, Coroutines do not waste resources of services and increase the performance of applications. Coroutines use atomic lightweight callback functions — continuations. Every continuation is managed by a state machine and can be suspended in the needs of service. It gives us the advantage of non-blocked threads services.
Did we manage to solve our problems with Coroutines?
Yes, we have! In a point of registration, we have to validate and revalidate a lot of incoming data across multiple services over and over. After a code analysis, we found that our validations are totally independent of each other and we as engineers can improve performance by providing an asynchronous way of communication between services, as a result of our code improvement with Coroutines, we got an incredible decrease of latencies around 40%. And it means our drivers have to spend less time in the registration process.
How did we manage to achieve it?
I will try to explain it with examples of one of our information services. Let’s take a look at the example of the initial sequential code.
Code is easy to read and it contains a lot of external services API calls, as a result, we have a huge response latency around 1000ms per each request. Threads are blocked by other services until the response. We can find a way to fix it and one of the approaches will be the reactive pattern of communication between services.
Yes, right now we have an example of parallel non-blocking communication with a response time around 500ms, but code losses readability and business logic became much more complex. So we can try to solve it with Coroutines.
As you can see the code didn’t change much from the sequential version, but we got a benefit of asynchronous communication between services. And here is one of the main strength of the coroutines approach, business logic can be clean, easy to read and asynchronous at the same time.
Did we find something unexpected while implementing Coroutines in our services?
Yes, we did! As a company, we are used to use MDC context for the logging process and with coroutines, we unexpectedly loosed it. It means we weren’t able to track our requests between services so easily as before. But after some research, we found a way to restore the context inside coroutines with additional Kotlin library for MDC context.
Are you interested in opportunities in mobility? Check out the open opportunities available across Europe and in our tech hubs in Barcelona, Berlin and Hamburg here.