Kotlin as an option for service creation at Gympass

--

Introducing the Kotlin language as an option for service creation at Gympass.

Reference: https://unsplash.com/photos/azJJSiwWW90
Reference: https://unsplash.com/photos/azJJSiwWW90

Authors: Rafael Pereira Santos, Rafael Sienna, Diego Caxito

Abstract

Some months ago, we at Gympass began to see that some parts of our code, written in Ruby on Rails , were large and complex. This demanded a split from a much bigger service we had at that time to a smaller one, a microsservice specific to these roles (and if you were thinking of a monolithic transition to a microsservice — you’re right).

We began discussing what alternative language and what stack could be used in this new service we were planning to build. Some of them were already well known by the team, such as Ruby on Rails , Scala with Akka and NodeJs , while others came from personal experience by team members, like Kotlin , and others just piqued our curiosity, like Typescript .

In this post we will describe our perspective on our ecosystem and how our engineers worked to introduce this new stack and these new technologies in order to improve their own working life and the overall quality of our product.

In the beginning…

At that time the mainstream languages were Ruby and Scala. Our entire team was well versed in these languages and their stacks and we thought it would be no problem to implement a new service with it. We also thought, though, that this would be a great opportunity to try something new, so we took a shot and began exploring new options. The ones that surfaced were Kotlin, Typescript and Go. The first two were recommended by members of the migration team, while the last one was making inroads at Gympass and was, at that time, relatively new .

After some tests and discussions among team members, we began to consider Kotlin together with Spring Boot (alongside other smaller frameworks and libs that will be discussed later) as a candidate.

In order to decide what languages and stacks would be used, we defined a few criteria: all had to be easy to learn and not that different to what we had at the time (in order to create an entire rest service in C) and they had to perform at the very least no worse that the stacks we had. We also set on a process to ensure a new stack was sustainable and properly automated.

Productivity

Another point that supported our decision was the fact that we already had some know-how and experience with the Java world, so there would be a less steep learning curve using Kotlin if compared to Scala or Go. And even those team members that were not experienced would be able to learn easily because of the shallow learning curve and the support of the Spring Boot community both in Kotlin and Java.

In order to try it out, as shown above, we decided to start a test project from scratch in order to learn if we could create a simple RESTful application with ease. One of our first observations was that it was very easy and quick to create, using the Spring Boot framework, because of its philosophy of convention over configuration . Even so, when trying to drop the configuration, which can be desirable in some cases, the configuration is simple enough that it hardly warrants any problem.

Thanks to this philosophy, we have a lot of plug ‘n play libraries, usually with the starter suffix, that just by a simple dependency declaration does all the needed setup related to this aspect of the application. A good example of these are the , Spring Webflux and libraries, which allow us to serve with minimum boilerplate reactive endpoints, or endpoints like / Spring Admin health and / env Spring Actuator and / metrics , or helping us monitor the state of our application more thoroughly.

Even better, we already have Actuator and in place within the Gympass ecosystem. Through our internal , we can check the application status and perform some administrative tasks such as changing the log level, extracting the thread dump, cleaning the cache, etc. It’s all available by just adding a couple of dependencies .

Image: Gympass Actuator Panel.

Cost & Efficiency

Inside Gympass

As mentioned before, we already have an application in production with Kotlin and Spring Boot, with an following it . It seems that the most requested endpoint from the application ( GET /partners/presentation ), has a very low latency, with a P95 of 17ms on the week of June 24th, 2020.

If needed, we can see the memory footprint of the application inside the containers view:

partner-content: Kotlin service

As can be seen, the memory footprint isn’t really small but it is really manageable, using a lot less than 1GB, the limit of a t2.micro EC2 instance . Initially we thought it was significantly more than Scala, but when searching the data about other Scala applications that use Postgres too, the datastore used by partner-content-api, we found services from 400MB to 800MB:

client-billing: Scala service
retro-service: Scala service

Looking at this data, the memory footprint of the services seems to be comparable, although we can imagine that the Spring Boot applications will have a minor extra memory consumption if we don’t do any tuning. And as an outlier, the eligible service uses4 to 5 GB of RAM, but it is an extreme case that deals with very large files.

Benchmarks research

Frameworks

We also had to decide what framework to use with Kotlin. We performed some internal benchmarks, but there are some good ones available out there — , this one measures Kotlin using different frameworks including Spring Boot. You can watch this , alongside this . It explores some aspects, like boot time, memory consumption and response time in the JVM: repository

Reference: video

Spring Web MVC and Webflux

  • Servlet Blocking
  • Servlet with CompletableFuture Java 11 Client
  • Servlet with CompletableFuture Apache Client
  • Webflux with Java HttpClient
  • Webflux with Apache HttpClient
  • Webflux with Spring Webclient

As can be seen, the tests were divided between 3 different implementations using Web MVC(Servlets) or Webflux. For each setup there was a battery of tests with a parameterized concurrent client number, with an IO call that took a parameterized delay time. The implementations had the following throughputs:

For a bigger version: https://miro.medium.com/max/1000/1*-h-vju5F-J7_BzjzghCNHw.png

If the image is unclear, please refer to the link under the image. In summary, it shows that in most of the cases, the Webflux versions prevail over the Web MVC versions of the application, except for those that use Java 11 HttpClient on Webflux. It shows that the use of Webflux with Apache HttpClient and Spring WebClient are comparable in question of throughput. It can be seen too that the throughput is smaller than the one given in the benchmark above. This can be explained with two details: this current benchmark does an IO call in the middle of the request, when the benchmark above is purely CPU bound, without json serialization.

Another detail this benchmark looks at is the memory footprint, with almost 60 charts. We won’t show them here, but in short, they demonstrate that the applications don’t vary too much in memory footprint with the scaling of concurrent users (in tests with 100, 200, 400 and 800 concurrent users).

To summarize, if we are interested in the most responsive application, it would be in our interest to select the completely reactive alternative like Webflux, rather than a Servlet one, or even a Servlet alternative using reactive elements, like CompletableFuture. When comparing a completely reactive approach to a Servlet with reactive calls approach, it seems in cases of low IO delay, the throughput can become 25% lower. In cases of more IO delay inside the requests, the difference becomes surprisingly smaller.

It is also important to quote here that it seems that at the time of that benchmark, the Java 11 HttpClient had serious performance issues inside Webflux, and sot seems that it is more secure to use third party libraries for HTTP, like Webclient, Apache HttpClient or, missing in this benchmark, OkHttp.

JDBC and R2DBC

Another interesting benchmark is (please try opening it in Chrome, as Firefox returns a 403 error) , comparing applications with a matrix of R2DBC and JDBC with Spring Webflux and Spring Web MVC.

In this benchmark, the writer tested the applications in increasing steps of concurrency, starting with 4 concurrent requests and evolving until 500 concurrent requests. In those tests, he measured for each combination:

For the sake of brevity, we will not show all the graphs here, only the throughput and the latency charts, but if more detail is desired, we recommend reading the complete article:

Reference: https://technology.amis.nl/2020/04/10/spring-blocking-vs-non-blocking-r2dbc-vs-jdbc-and-webflux-vs-web-mvc/
Reference: https://technology.amis.nl/2020/04/10/spring-blocking-vs-non-blocking-r2dbc-vs-jdbc-and-webflux-vs-web-mvc/

Observing those resources shows that the latency is better in any setup that uses R2DBC, achieving significant gains in their use. It also shows that in matters of latency, the use of Webflux is undisputed.

When looking at the throughput, it is confirmed that for a scalable system, R2DBC continues winning in one more question. Even so, the throughput chart shows that the combination of JDBC and Spring Webflux is worryingly less scalable and has a surprising drop in performance, even with its superior latency. It makes this combination hard to suggest in some cases.

Even with those demonstrable gains, there are even further issues, including this challenge when choosing R2DBC:
“ When Java Fibers will be introduced (Project Loom, could be Java 15), the driver landscape might change again and R2DBC might not become JDBCs successor after all.”

This could cause the problem of us having to revert back to another implementation in the future and although there is always hope that Spring will ease our lives and create another library that will allow us to change with minor changes, this is not yet confirmed.

Market adoption

Stack Overflow Language Trending: Kotlin, Scala and Go

According to Stack Overflow, these are the trends of the current languages mostly used by Gympass in Backend, Go, Scala and Ruby, and Kotlin:

Reference: https://insights.stackoverflow.com/trends?tags=kotlin%2Cscala%2Cgo%2Cruby

Stack Overflow Frameworks Trending: Spring-boot and Akka

According to Stack Overflow, these are the trends of the current frameworks mostly used by Gympass in Backend, Akka and Ruby on Rails, and Spring Boot:

Reference: https://insights.stackoverflow.com/trends?tags=akka%2Cspring-boot%2Cruby-on-rails

TIOBE has an index that excels in demonstrating interest in languages, as they rank languages by search queries in various search engines. Kotlin is ranked in 27th place:

We could find that the language is growing as referenced from the TIOBE Index for July 2020 :

“ […] Other interesting moves this month are Rust (from #20 to #18), Kotlin (from #30 to #27) and Delphi/Object Pascal (from #22 to #30)” .

Github State of the Octoverse 2018–2019

Reference: https://octoverse.github.com/#footnote--fastest-growing-languages

Thoughtworks Technology Radar — Kotlin

In 2018, Kotlin achieved the Adopt state in , indicating that Kotlin development was mature enough to be suggested as a new technology for development: “[…] With JetBrains adding the ability to compile Kotlin to Thoughtworks Technology Radar on multiple platforms, as well as , we believe it has the potential of much wider use by the larger community of mobile and native application developers.” native binaries transpile to JavaScript

Thoughtworks Technology Radar — Spring Boot

In 2016, Kotlin achieved the Adopt state in , indicating that Spring Boot development was mature enough to be suggested as a new technology for development: “A lot of work has gone into Thoughtworks Technology Radar to reduce complexity and dependencies, which largely alleviates our previous reservations. If you live in a Spring ecosystem and are moving to microservices, Spring Boot is now the obvious choice. […]” Spring Boot

Kotlin FAQs

Looking at Kotlin FAQs , we can find the following declaration: “ There are too many companies using Kotlin to list, but some more visible companies that have publicly declared usage of Kotlin, be this via blog posts, GitHub repositories or talks include , , or .”

After looking at the quantity of resources, there are a lot of other sources that indicate the same interest in Kotlin:

It seems that Kotlin is very well positioned in the majority of those rankings, and although sometimes it might be surpassed by languages like Go and Scala, it seems more consistent in those indexes.

Note, however, that Java is always near the top of every ranking, alongside Javascript. Kotlin isn’t Java, but a Java developer could easily adapt to a Kotlin environment as it has a direct interoperability model with Java.

Existing toolbelt to solve daily problems

Considering that we already have one technology stack up and running, we researched frameworks and libraries that could be adopted quickly within the stack to solve our daily routine problems during our services building. Below is a list with samples that we could find on this stack toolbelt to work on our daily routine. This list is dynamic, of course, and could change as our engineers begin to develop their components.

Kafka

Swagger

  • There are plenty of ways to implement Swagger using Spring Boot, and this one this one is a good example

Datadog

  • There is already step-by-step documentation about how to implement datadog in a JVM based application.

Dynamo

Elasticsearch

  • Elasticsearch has a dedicated page for supported clients.
  • There is a specific client for Kotlin: ES Kotlin, that allows the user to use their DSL to better use Elasticsearch.

Postgres/MySQL

  • There is a nice looking library called Exposed that seems to address a bunch of integrations.
  • We have the Spring Data JPA library, which allows us to create easy repositories with a minimum boilerplate. The only possible drawback is the fact that it works only in a blocking sense.
  • For a reactive SQL client, we have access to Spring Data R2DBC, which allows us to query MySQL and Postgres databases in a reactive manner, integrating better with Webflux. It is a little more verbose than JPA, but in case of a full reactive flow, it can be of interest.

REST Client

  • Retrofit is a library created in Java that gracefully attends this need, you can see examples of this use in the Kotlin spike in link and resources. This library supports blocking calls by default and reactive calls via coroutines, callbacks or, by means of adapters, Spring Reactor abstractions. In using Retrofit, we use the OkHttp Http client underneath the hood.

Testing

  • JUnit Jupiter will be our test engine.
  • JUnit 5 is a good option for a more traditional style of unit tests, running over the JUnit Jupiter engine.
  • For more complex integration tests, we can use the Spring Test Starter library, which works with JUnit 5 and automatizes to great lengths the process of configuring the environment, as can be seen in this tutorial.
  • For those accustomed with Rspec and Spec, there too exists Spek, a BDD style test framework, running over the JUnit Jupiter engine.

Some other frequently asked questions that we faced during the request for comments fase

Why not Micronaut?

Micronaut showed great promise during our research, with gains of memory footprint and throughput. Our discussions showed that we could see potential gains, but using Micronaut would deprive us of using all the know-how on Spring Boot available on the internet and on the market.

Because of this, we chose to use Spring Boot as the framework, so we could choose a path that will allow us to move as fast as possible with a great degree of quality, in a way that will be welcoming to every new developer approaching the stack for the first time. And although it is not the most efficient route, we believe that the gains in developer productivity will outperform the gains in memory footprint and response time.

We might discuss this approach in the future, depending on the evolution of Kotlin inside Gympass, but the decision seems like the correct one for now.

Spring Webflux or Spring Web MVC? Blocking or not Blocking?

At first, it seems like Webflux is the obvious choice, a framework that seems to outperform Web MVC in every aspect, even when the Spring Web MVC vs Spring Webflux issue is raised.

Even so, analyzing the two worlds, it seems that not only there is a lot of overlap between the two libraries, but Webflux has some drawbacks too:

Venn DiagramSpring MVC or WebFlux?

It seems that Spring MVC works best with libraries that are mandatorily blocking, like JPA, JDBC and others. Not only that, but a non reactive code is a code easier to understand and debug, as reactive code can be running on a thread pool with an infinitude of threads over an event loop. For both Reactor and coroutines, we have stack traces of diminishing investigative power, lowering the transparency of how the application is running and how errors happen. There is even the risk of some Spring functionalities not existing inside Spring Webflux but existing inside MVC, and the process of filter creation being harder inside the Webflux environment. It seems like Web MVC is a more beginner friendly library than Webflux.

Outside of that, some very important aspects to be in favor of using Webflux. By using Webflux we gain access to the creation of routes via a routing DSL that reminisces Akka routes model. And just by using not only Webflux, but libraries that do IO in a reactive manner, we gain APIs that scale better with more concurrent users, which can be game changing in projects that have a high degree of concurrent requests.

After listing those arguments of both web frameworks, it seems that the balance between the two is closer than at first sight. Even so, one would argue that Webflux by default seems to already offer throughput gains over Web MVC just by being used, outside of cases that use JDBC, as shown on our benchmarks. There is the fact that Webflux allows the user to use the @Controller syntax already popular with Web MVC, diminishing the gap between the two. We could even say that we can make the same blocking endpoint in both with minimal differences this way, and that sometimes it is a valid choice, as the complexity, debugging and error discovery of endpoints.

  • A request that is purely CPU bound? Imperative.
  • A request rarely called, with little or no IO and complex logic? Imperative with a beginners team, reactive with a more experienced team.
  • A request that is moderately called, with IO inside it and complex logic? Imperative with a beginners team, reactive with a more experienced team.
  • A request that is moderately called, with IO inside it and no complex logic? Reactive.
  • A request that is known to be a bottleneck, with any non zero quantity of IO inside it? Reactive.

It seems that the best choice is defaulting to Webflux and using the simplest way to solve the given task, as we must consider not only the performance, but how much it needs someone with a higher level of know how to understand what is happening and maintaining it:

This choice is not as clear cut as the case of use of Spring JDBC/JPA instead of R2DBC in cases of MySQL/Postgres access, because it shows that MVC outperforms Webflux in those cases.

Conclusion

To summarize, after all this research, we decided to begin adopting Kotlin with Spring Boot as one more alternative to creating services within the Gympass product development team and to start using it as a fresh new service.

All of the content and research shared here is just a small sample of what Kotlin offers. It should meet what we need at Gympass, but there isn’t really a perfect language or framework that would meet all of our development issues. Kotlin meets some of the requirements looked at when choosing a language.

This post was the result of an entire team of developers from different areas coming together to contribute with ideas and proposals. We would like to thank Gabryel Monteiro in particular for his willingness to share his expertise with Kotlin and pointing it out as an alternative to us.

Links and resources

Originally published at http://docs.google.com.

--

--

Rafael Pereira Santos
Wellhub Tech Team (formerly Gympass)

Web developer, backend and passionate by programming, currently working at Gympass