Oria

Oria (oh-rye-uh) is an in-memory, Ruby-based Key-Value Store. It's designed to handle moderate amounts of data quickly and easily without causing deployment issues or server headaches. It uses EventMachine to provide a networked interface to a semi-persistent store and asynchronously writes the in-memory data to YAML files.

Installation

Oria is provided as a Gem. Use the following command to install it:

gem install oria --source http://gemcutter.org

That's it! You're ready to start storing basic data in memory. Now add

require "oria"

so's you can access Oria.

Command Line

The recommended pattern for starting and stopping Oria is using the Oria command line application, like so:

$ oria start|stop|restart

That's it! Oria will run in-memory, and you can always shut it down using oria stop.

Auto-Start and Daeomonizing

Yes, I recommend you use the command line. But on certain platforms, you don't have access to the command line. And since Oria was built to be simple to use and to deploy, it also supports auto-starting and stopping. It will detect a downed server and boot itself up in a separate thread. I should warn you, however: this functionality means Oria requires a *nix environment - sorry, IronRuby users!

Oria auto-starts transparently, so just use it normally to take advantage of this feature. Note: this feature is currently untested, so please test it heavily before deploying, and report any issues you may have!

Usage

Okay, now for the fun part. Oria behaves (mostly) like a Hash - you could say that it responds to 2/3 of @wycats' Moneta plugin. Specifically, it responds to the following Hash methods:

[]=(value)          Set a key to ... something.

[]                  Retrieve a key

delete(key)         Delete and return a key's value

key?(key)           Returns a boolean value for whether or not that key exists

has_key?(key)       Same as key

clear               Clears all keys and values from Oria

In addition to those methods, Oria also supports a cool option inspired by some other KVS's:

stash(value)        Stash a value in Oria. Returns the randomly generated key it stored the value under. This
                    is useful for when you need to store something temporarily, e.g. stash it, pass the key
                    in a URL, and retrieve / delete it.

So let's play:

Oria[:foo] = 'bar'  #=> "bar"
Oria[:foo]          #=> "bar"
Oria.key?(:foo)     #=> true
Oria.delete(:foo)   #=> foo
Oria[:foo]          #=> nil
Oria.stash("baz")   #=> "wZ"

Nothing exciting? Try shutting your app down and booting it back up.

Oria[:wZ]           #=> "baz"

Bam. A relatively fast KVS with no configuration or special server setup.

But wait, I stored a ! WTF?

Ah yes. You've found Oria's Achilles heel. Oria speaks JSON, so everything you give it must be capable of JSON'ing. That means Oria[:user] = User.find(1) ain't working any time soon. Likewise, and perhaps more unfortunately, things like Oria[:my_cool_hash] = {:key => "Key!!!!!1", :value => "valyooooo"} are going to return {"key" => "Key", etc...} so your hashes are going to respond to string keys and not symbols once they've been through Oria.

It's a bummer, I know. But this is an in-memory KVS, and not Rails sessions where we're marshaling and un-marshaling everything every request. I realize that it'll most likely only ever speak to Ruby clients (specifically this one), but this is where it is. Sorry.

Configuration

Oria is built the be configuration-less out of the box, but if you really need to, you can tell it to do lots of things. It's built on top of EventMachine, so networking is an option - but if you're networking your KVS, you should think about upgrading to something like Redis or Memcached. Oria is meant to be used in situations where a database OR a high-powered KVS would be overkill. But then again, you can use Oria to decentralize some of your tasks over a network, which is fun. Observe:

Oria.connect(server, port)      Connect to a server / port. Defaults to localhost and 6851

Oria.disconnect                 Kills the running server... maybe (see command line vs auto-start above)

Oria.app_key = value            Oria supports "splitting" your apps, much like how Resque supports named queues. Specifying an
                                app key will effectively change the hash you are working with. It defaults to "default," cause
                                I'm original like that.

Let's try it out:

Oria.app_key = "my_app_1"
Oria[:foo] = "bar"
Oria[:foo] #=> "bar"

Oria.app_key = "my_app_2"
Oria[:foo] #=> nil
Oria.app_key = "my_app_1"
Oria[:foo] #=> "bar"

That's it! I hope you enjoy Oria, and please let me know if you find any issues or have any trouble. As you will no doubt see from the current version information, it's a very young project, and any contribution is welcome.

Dependencies

Oria speaks JSON, so it relies on the JSON gem. It depends on the newest stable version (1.2.0), so be sure to add the right version checking code to your legacy apps before using Oria!

It also needs EventMachine to do everything. Yes, I could have used Drb or straight UDP sockets, and spent a lifetime on this. But EventMachine is seriously, seriously, seriously awesome, and works very well without me writing an insane amount of code I couldn't write very well anyway. Check it out and see for yourself.

Warnings

Oria is weak in a few places. First, it forks all over the place with no regard for the consequences. Someday I'm going to upgrade it to use Daemons, but not right now. In general, the forking is dangerous and clumsy.

Also, Oria's auto-start feature relies on sleep. In fact, Oria relies on sleep in more than one place. This is terrible and I want to fix it someday. But I also want to get the code out there so people can start breaking it and tell me where it breaks.

Which means: please report any problems you have using the Github issue tracking!

Benchmarks

Want to find out how well Oria will perform compared to existing solutions? Here are a few benchmarks.

Here's Redis and Oria:

                     user     system      total        real
Redis (write):   0.040000   0.020000   0.060000 (  0.112147)
Oria (write):    0.130000   0.100000   0.230000 (  0.394356)
Redis (read):    0.070000   0.020000   0.090000 (  0.140881)
Oria (read):     0.130000   0.100000   0.230000 (  0.394533)

Obviously, Redis is going to smack Oria down no matter what. Still, it's in a competitive range.

Here's MySQL and Oria:

                     user     system      total        real
MySQL (write):   0.260000   0.030000   0.290000 (  0.423971)
Oria (write):    0.140000   0.100000   0.240000 (  0.406949)
MySQL (read):    0.310000   0.020000   0.330000 (  0.468195)
Oria (read):     0.100000   0.100000   0.200000 (  0.330884)

Oh snap! Oria outperforms MySQL. How about SQLite3, in memory?

                      user     system      total        real
SQLite3 (write):  0.360000   0.010000   0.370000 (  0.376660)
Oria (write):     0.100000   0.100000   0.200000 (  0.368118)
SQLite3 (read):   0.370000   0.010000   0.380000 (  0.379038)
Oria (read):      0.150000   0.100000   0.250000 (  0.406342)

Looks like Oria will write faster but read slower than SQLite3 in-memory.

Copyright (c) 2009 Flip Sasser, released under the MIT license