Scala application lifecycle, from pure Scala to FS2

Michał Matłoka
SoftwareMill Tech Blog
4 min readOct 31, 2019

--

In the previous blog post we have described the Scala application structure, now we’re going to focus on the whole lifecycle, including the initialization and shutdown.

Lifecycle

During application startup, there are usually multiple typical operations to be performed. First, it is needed to load configuration, usually from resource files. Then database clients are being created together with connection pools. HTTP server must be started to provide APIs to other services. An application may have stream processing e.g. of incoming Kafka messages, which also would need to be started. Apart from that, there are things like monitoring or just HTTP client connection pools initialization to other services. However, there is one common thing among all the mentioned operations. At some point, which usually would be the shutdown, they should be closed. All files, connections, sockets etc should be gracefully closed freeing system resources.

Take a look at my blogpost “A short story about resource handling” describing different approaches to resource management in various technologies. You’ll find there more detailed descriptions of e.g. Cats Resource or Bracket.

Plain scala

In “vanila” Scala we just put all initializations into the Main of your application. Resources closing is however more difficult. The usual way is to use a shutdown hook.

Sample scala DI — Grafter

My other blogpost describes the Grafter DI. The one thing which I want you to notice here is, that it includes Start and Stop traits allowing to define appropriate logic on module level. Grafter will manage the lifecycle and execute methods in appropriate point in time. This approach is more similar to ones you may know from Spring or Java EE. However, I would rather propose to avoid it, due to its quite specific design decisions.

Cats Effect world

Cats Effect allows to encapsulate any side-effects in an IO type. IO[String] means that once you run the IO, the result will have the String type. However, without explicit running, nothing will happen, everything is just lazy. You can think about it a bit as a lazy Future. However, cats-effect includes a lot of other useful features.

Resource and Bracket classes allow for safe usage of resources requiring acquisition and release after usage. They wrap them, and after the use wrapped objects are being automatically closed. There is no way to consume the value without releasing the resource. More info also here.

Cats-effect library provides an additional class called IOApp allowing to return IO from your application. This way you may wrap all your side effects. However, the most important fact is that you can easily combine Resource or Bracket into your application.

This also means that your application may become a composition of big for comprehension over IOs.

The only drawback here is that you can’t just flatMap the Resource, you need to use theuse method. What is more, every leveraged fs2 stream needs to be compiled and drained to get the IO. This concerns things like HTTP server, Kafka consumers and other long running processes.

FS2

FS2 is one of the Scala stream processing solutions. It is usually compared with Akka Streams or Monix. FS2 integrates easily with Cats-effect.

However, there is another way. To define most of the operations as operations on fs2 streams. fs2 provides methods allowing to evaluate IO during the Stream processing (Stream.eval), but also allows to convert cats Resource to fs2 bracket using Stream.resource method.

FS2 bracket is very similar to the Cats Bracket. You can define a special Stream whose element includes acquiring and releasing logic. After consuming the element will be automatically released.

def bracket[F[_], R](acquire: F[R])(release: R => F[Unit]): Stream[F, R]

This means FS2 and Cats can be easily integrated.

This way, you may have a single for-comprehension and in the end, you need a single compile.drain (stream evaluation) to get an IO. For sure such an approach guarantees you that your resources are safe, and that they are going to be released on application shutdown.

Conclusion

Remember that every opened resource should be eventually closed. FP may help you with that but has a more steep learning curve.

The presented application structures & organizations are not the only way to go, but may become the choice for your project. The included gists are pieces of bigger examples presented in the github repository. Please note that all provided code examples are simplifications allowing to present concepts, not full solutions. Their aim is to present a compiling structure, not a running application. They could be greatly improved before going to production.

Take a look at the repository presenting broader examples for the shown concepts.

--

--

IT Dev (Scala), Consultant, Speaker & Trainer | #ApacheKafka #KafkaStreams #ApacheCassandra #Lightbend Certified | Open Source Contributor | @mmatloka