Last time in our project we had to add progress bar for visualization of long time running process. Process was made of a few phases and we had to print in which phase we currently are. In first step we conclude that we need to create a class of Progress which will be passed as an implicit parameter to our service. Then we will wrap method calls be inProgress
method which will notify some e.g. akka actor about phase begin and phase end.
But this approach has some disadvantages. Firstly before we start service's operation we need to init progress with count of all phases to get know ratio of progress finish. With this approach we had to add some extra counting before operation start.
If we want to keep real progress notifications the numbers of phases had to fit count of inPhase
blocks. Some of phases were dynamically computed and some where omitted in case of failure validations results. This code become to be unmaintained.
We found that we need to join computation of phases with real phase processing. In this case we need to change approach from building process to building chain of phases that will run the process. Each phase will take the result of previous phase and transform it to new output. So example process will look like this:
Code giving this chain functionality looks like this:
We've used right associative operator :: for building chain of phases. "Body" of phases is piped by andThen
: processPrevWrapped andThen processNext
. For nil-tail we need to have a factory creating empty chain with identity "body" function.
Also if we have this kind of tool, we can modify piping code according to nature of our flow. For example if we are using scalaz.Validation we can do validating chain which will extract a success from n-step output and pass it to input of next step (like flatMap). In the other hand if n-step will return Failure, we will skip all remaining phases of validating chain.
To make building of chain more production-ready we add some extra features:
- Chaining of chains (sth like ::: in scala Lists)
- Transforming of input/output - for adding some "glue" code for simpler phases chaining
- Wrapping of chains - also some "glue" code doing both input and output transformations
- Sequencing of chains - sequenced processing of multiple phases with the same input
If you are interested in using similar approach, take a look at my github project: scala-phases-chain
. If you want to integrate this tool with akka actors, simply change MultiPhasedProgress.notifyAboutStatus
method to look like this: