Eat your evil! Face your frog!

… or the other way around! Doesn’t really matter that much. What matters is to discover a good enough software architecture that we afford building and covers as much as the architectural requirements at the same time. In our case, the infrastructure limits plays a crucial role but unfortunately, this is not the best match with all the functionality we want to deliver.

In my first post, I have mentioned my design strategy of generating static content as much as possible and to avoid server-side request processing. Apparently, we cannot hold that promise for long as there is one exception that we cannot compromise. Guess what! It is the audio streaming itself. We use Icecast server to broadcast our stream. It starts its own HTTP server and serve clients with audio streams (in our case a MP3 live stream).

Now we need to feed icecast with programs. We create our source stream using liquidsoap tool (which is a very cool piece software BTW). Liquidsoap provides an internal DSL to define playlists, multiple input sources and playback queues, and allow you to switch between them programmatically.

Let’s now combine our findings from above with our decisions from our first blog post and draw a sketch of runtime components.

Figure 1. Component view of the architecture for RAA. All unnamed boxes represent one or some NodeJS scripts
Figure 1. Component view of the architecture for RAA. All unnamed boxes represent one or some NodeJS scripts

This napkin sketch has many interesting details worth discussing. In the heart of our system is a script named raa.js. This is the entry point of our radio and is responsible of generating lineups each day and scheduling them. Let’s write down its main responsibilities:

  1. Decides about the lineup for a given day (a post about lineup generation will come soon!)
  2. Generates HTML representation of the lineup, publish podcast links and other supported on-demand distribution methods
  3. Schedules each program for playback at the time of the day decided by our lineup planner in step 1
  4. Stays alive, watches the template files and regenerates lineup if there is a change in plans

When I come from, scheduling is usually done be libraries like Quartz. But I guess we agree that a radio does not have millions of programs playing during one day. So why bother with all the middleware and not stick to the good old Cron jobs? Every playback job executes a script upon triggering by the system. This script:

  1. Adds program clips to the liquidsoap playback queue
  2. Generates static HTML that displays the currently on-air program

Finally, liquidsoap may call another set of RAA scripts to query and update clip metadata from our lineup.

Cool! We now have a radio that plays programs on schedule and publishes its daily program as well as live status of what’s being played; and all without spending our resources on expensive middleware.

Resource usage state: Our code and all the tools used with it, occupy ~250MB of RAM. Also, the peak CPU usage of our node is consistently below 20%. Adding up the resources required by the operating system (~500MB for Ubuntu Server 16.04), we still have room for ~250MB worth of new features which will take quite a few blog posts before it exhausts :-)

- Back -