avatarSupratim Samanta

Summary

Rio is a Go library providing a lightweight job scheduler with asynchronous job processing, retry, timeout, and context cancellation features, designed to simplify job chaining and management in Golang web applications.

Abstract

Rio is designed to address the challenges of managing multiple asynchronous jobs and data transformations in web applications by offering a job scheduler with built-in functionalities such as retry, timeout, and context cancellation. It is particularly tailored for Golang web apps but can be adapted for any application requiring job scheduling. Rio introduces a priority queue for handling incoming requests and manages workers efficiently. It also simplifies the chaining of dependent jobs using closures and function types, allowing for sequential execution in a single goroutine, which can be more efficient than managing multiple goroutines with data dependencies. The library provides an easy-to-use interface for chaining jobs and handling their responses, whether it's a single or multiple job responses, and it gracefully handles job failures by returning an empty response.

Opinions

  • The author suggests that traditional methods of handling multiple data sources and job scheduling in web applications can lead to unmanageable code, especially when using goroutines without proper management.
  • Rio is presented as a solution to reduce boilerplate code and simplify the process of transforming data between services, making the codebase easier to maintain and update for new team members.
  • The author implies that chaining jobs in a single goroutine, as facilitated by Rio, can be more efficient than the conventional approach of waiting for each service call to complete in separate goroutines, due to the data dependency between these calls.
  • The author emphasizes the ease of use and the benefits of Rio's pattern for chaining dependent jobs, which ensures that jobs are executed in the correct order and that data is passed seamlessly between them.

rio — A lightweight job scheduler in Go with batteries included

Photo by Joey Kyber on Unsplash

What is RIO?

Rio is a lightweight job scheduler and job chaining library. Its mainly build for Golang web apps, but it can be very easily mold to serve any application needing job scheduling. The library is an asynchronous job processor, which makes all the backed calls asynchronously with retry, timeout and context cancellation functionality.

It also provides very easy semantics to join multiple data sources based on their output and input types, at the same time having no coupling between the data sources. This helps in creating new APIs or resolvers for GraphQL APIs a breeze.

Concern

Many times we write web apps which connects to different data sources, combines the data obtained from these sources and then do some more jobs. During these process, we do a lot of boilerplate to transform one data type to other. Also in the absence of a proper job scheduler, we create goroutines abruptly and without proper management. These create unmanageable code. To update those code is even more hard in future, when there is a new team member in the team.

Rio tries to solve this problem by introducing two concepts.

1. An asynchronous job processor

This is the piece which runs the multiple jobs asynchronously. It has a priority queue(balancer.go and pool.go) which hands off incoming requests to a set of managed workers.

The balancer is implemented by a min heap priority queue and when assigning a new task it checks the least loaded worker.

To implement the min heap we just need to implement 4 handy methods of the pool interface like this:

2. Easy management of these goroutines and chaining them

How many times do we do this:

call service 1 in goroutine 1
wait and get response from goroutine 1
call service 2 in goroutine 2, taking piece of data from service call 1
wait and get response from goroutine 2
call service 3 in goroutine 3, taking piece of data from service call 3
wait and get response from goroutine 3

You get the idea, this only delays thing more and does a lot of context switching. Rio helps in this, by chaining multiple calls together by means of using closures and function types and runs in one goroutine.

Now many can think is it not going to be slower compared to doing multiple goroutine calls.

Let’s see.

Think of the previous example. If you do not get response from service 1, can you invoke service 2, or if service 2 fails, can you call service 3? No right, as there is data dependency between these calls.

Rio chains dependent jobs together by introducing this pattern.

request := rio.BuildRequests(context,
          (<callback of service 1>.WithTimeOut(100 ms).WithRetry(3))
          .FollowedBy(<function for transforming data from service 1 response to request or partial request of 2>,
                      <callback of service 2>)
          .FollowedBy(<function for transforming data data from service 2 response to request or partial request of 3>,
                                  <callback of service 3>)

Let’s see an example

Once the chaining is done in line 10, we are posting the jobs like this

balancer.PostJob(request)

And finally waiting for the chain to complete

<-request.CompletedChannel

Once the call chain happens, the request comes back with responses for all these calls in a slice and we can do this

  • Only one job response
request.GetOnlyResponse()
  • Multiple job response
request.GetResponse(index) //---0,1,2

If any job fails, the response will be empty response, specifically rio.EMPTY_CALLBACK_RESPONSE

Watch out for the full example in the example folder if anyone wants to use it.

Thanks !!!

Some of my other Golang articles:

Golang
Scheduling
Asynchronous
Web Development
Go
Recommended from ReadMedium