1. Ione v1.2, TLS support, and more

    Ione v1.2 was released a little over a month ago and I forgot to announce it here. It now supports making TLS connections and starting TLS servers.

    There’s also some additions to the futures API and the timer scheduler is no longer unusable.

    TLS

    Starting a TLS server or opening a TLS connection is now almost as easy as starting any server or opening any connection. The only thing you need to do extra is to create an OpenSSL::SSL::SSLContext:

    ssl_context = OpenSSL::SSL::SSLContext.new
    ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
    ssl_context.cert_store = OpenSSL::X509::Store.new
    ssl_context.cert_store.set_default_paths
    f = reactor.connect('google.com', 443, ssl: ssl_context)
    f.value # => an open TLS connection
    

    Futures

    Futures got some optimizations and new features. You can now compose operations with #then, which works like a mix between #map and #flat_map. It can be very convenient in situations where you may want to perform an additional asynchronous operation depending on the result of the previous operation. Before you had to use #flat_map and return a pre-resolved future in the case where you did not.

    For example, maybe you’re loading something from a database, and when it’s not what you wanted, for example an empty result, you load something else, as a fallback. It might look like this:

    f = load_the_thing
    ff = f.flat_map do |result|
      if result.empty?
        load_fallback_thing
      else
        Future.resolved(result)
      end
    end
    

    You need to use #flat_map because otherwise there’s no way to chain another asynchronous call – but it also means you must return a future even when you don’t want to chain. With #then you’re not required to:

    f = load_the_thing
    ff = f.then do |result|
      if result.empty?
        load_fallback_thing
      else
        result
      end
    end
    

    The futures API also got two other additions: Future.traverse and Future.reduce. The former is a combination of Enumerable#map and Future.all, and encapsulates a pattern I use all of the time: mapping an array of queries or IDs to an array of futures that loads something from a databased for each query or ID, and then using Future.all to create a single future of all of the result – Future.traverse does that elegantly in one go:

    f = Future.traverse(ids) do |id|
      load_thing(id)
    end
    f.value # => [thing, thing, thing]
    

    Finally, Future.reduce takes a list of futures and as their values become available it reduces them down to a single value, like Enumerable#reduce.

    Timers

    In previous versions of Ione the timer scheduler was extemely naïve. It was basically just an array of timers, in the order that they were scheduled. For each tick the reactor would scan through the array to find any expired timers and trigger their callbacks. It would also not remove the timers, just mark them as triggered, so the next tick would have to scan through the same array again. When a new timer was scheduled the array was compacted and all triggered timers were removed.

    There were two reasons for this implementation: the first was that I wanted to avoid locking in the reactor thread, which was possible using this metod, and the second was that I just didn’t consider timers a very important feature and didn’t have time to make something better.

    Not surprisingly timers were basically useless, and when people started using timeouts in cql-rb and the DataStax driver it got ugly. I had not planned to do anything more than TLS in v1.2, but when I realized how bad the situation was I read up on priority queues, implemented a heap data structure and replaced the scheduler. This delayed v1.2, but it was really worth it.

  2. Ione v1.1 released – the server edition

    Ione can now be used to create network servers, in addition to clients. The v1.1 release is the big first step towards making Ione the general purpose reactive toolkit for reactive programming in Ruby that I want it to be.

    There is a simple message of the day example server that shows the basics of starting a server, but I’ve also been working on a higher level abstraction for writing RPC clients and servers – still a work in progress, but check out the example to see how it works.

  3. cql-rb gets a little sister

    cql-rb v2.0.0.pre1 has just been released, it’s the first version with an external dependency.

    Before I tell you more I want to give you some background about why cql-rb has until now been completely standalone: When I started cql-rb I was hoping to use some existing gems for things like futures, IO, byte buffers, UUIDs, etc., but I didn’t find any that fit.

    Keep reading