Thursday, February 25, 2010

The Holy Grail Of Persistence?

One of the very first CTO-grade decision I had to take in the making of Snoget was to pick what would become our main transactional persistence engine. Since we're using Erlang exclusively for our production servers, the solution seemed easy: use Mnesia. But I settled for PostgreSQL.

At this point, anyone who's been dealing with O/R mapping (like Ted Neward who said: "Object/relational mapping is the Vietnam of Computer Science"), should cry fool: Mnesia would offer me persistence without any impedence mismatch with the application runtime environment and I preferred a SQL database to it? Actually, to someone who has used an O/R mapper before and who switched to Erlang, discovering Mnesia for the first time is a sheer heavenly moment similar to that:


The Holy Grail Of Persistence

Though Mnesia is very clearly not presented as a replacement for general purpose RDBMSes, one can not avoid to seriously consider using it, just because there is such a low cost into moving data from and to an Erlang application.

As a developer, I already had my share of joys and pains from working with non-standard persistence engines (like Tamino and X-Hive). I also learned from others who did the same, in much greater scale than me, and who shared their experience about it. So it is with great circumspection that I approached the decision of using a niche database engine instead of a mainstream one.

That being said, here are the four key decision points that made me favor PostgreSQL:

  • Schema Migration - For a startup, it's critical to be able to evolve a database schema with the less friction possible as features are often in a state of flux.

    Using a standard DB like PostgreSQL allowed us to leverage Ruby's ActiveRecord Migration, which is not only handy for migrating forward (as you do in production) but also backwards (as you sometimes have to do in development). Though Mnesia record evolution is possible, the fact that data migration concerns permeate into the application code is very unpleasant. Going schema free was a tempting option but would not have come close to the flexibility ActiveRecord and PostgreSQL gave us.

  • Supporting Resources - Being able to solve problems quickly is essential for a startup: for everything that is not your core business, you usually rely a lot on the information available out there.

    PostgreSQL has an extensive body of knowledge available online and in print. When things go haywire or in case of doubt, you're pretty much guaranteed that a Google search will bring you at least a couple of pages where people asked the exact same question and got answers for them. With Mnesia, the amount of available information is way reduced, simply because it's still very much a niche database.

  • Standard Connectivity - When you're focused on building something new, the last thing you want is wasting time in re-inventing the wheel: interoperable building blocks are key.

    Using an standard database like PostgreSQL gave us immediate access to tools like Pentaho's Data Integration, which we use to massage data. Though we could have built an army of supporting tools to perform the same on Mnesia, it's always better to use something that's already there. I has also allowed us to fully leverage Ruby On Rails to build an awesome back office in no time. Though there are some Ruby-Erlang bridges out there, none gives you all the RAD features you get when plugging Rails to a standard database.

  • Operational Simplicity - In a startup, there's no DBA to nurse your database engine: you have to deal with it so it better be simple to operate.

    Installing, upgrading, backing-up, restoring PostgreSQL databases are all well defined operations, supported by a wealth of tools. The security model is straightforward too. And there are plenty of options for monitoring what's happening under the hood and analyze and tune performances. I have no doubt all this is possible with Mnesia, but in a less familiar and straightforward manner.
Of course, there is a downside in using PostgreSQL with Erlang, and a pretty big one: there is no official driver for it so you're fully subject to the talent of the developer whose driver you'll be using. For us, it quickly turned out that the driver we started with was the Achilles' Heel of our application and we had to switch to another implementation, which turned out to be very solid. The switch was painful because there is no such thing as edbc, i.e. a standard for database connectors in Erlang. If you switch driver, you get a new API!

At this point, some pundits must be fuming and asking why SQL? What about NoSQL? Partially for the same reasons quoted above. But more importantly, we're not locked with PostgreSQL: we mainly rely on this database engine for its transactional capacities, not for its relational ones. If the need arise, the way our application is architectured would allow us to swap-in another persistence engine, provided it's transactional, one functional domain at a time and this without too much pain.

Finally, if you wonder if I picked up PostgreSQL because I was familiar with this database, the answer is that I never used it before. But nothing looks like a RDBMS than another RDBMS. Granted they don't shine like the Holy Grail, but still they'll happily power your software house.

Wednesday, January 06, 2010

Monitoring RabbitMQ with Zabbix

If you use RabbitMQ as your message oriented middleware and Zabbix as your monitoring and graphing tool, you're probably wondering how to monitor the former with the latter.

Here is the Zabbix Agent configuration I use to keep track of the number of messages pending delivery and the total number of queues (this second parameter may not make sense for you if you don't create a lot of dynamic queues):



As you can see, these user parameters are parameterized: they take a single parameter being the virtual host path that you want to monitor. Note also that the zabbix group must be added to the non-password sudoers for rabbitmqctl.

With these parameters in place, you'll be able to build graphs and set alarms for your favorite RabbitMQ virtual hosts!

UPDATE 10-FEB-2010: Alexis Richardson has been kind enough to point towards an SNMP plug-in for RabbitMQ that has been very recently released on GitHub. I have added a few features to it, so be sure to check my fork too.

UPDATE 04-MAR-2010: I'm now using the SNMP plug-in for RabbitMQ in production instead of the above solution, which is way more efficient. The use case for the above would then be only when SNMP is not an option for you.

Wednesday, December 16, 2009

Is Test Overlap A Necessary Evil?

In a recent blog post titled "The Limitations of TDD", Jolt Awards colleague Andrew Binstock shared some reservations Cédric Beust has about TDD. When a person of extensive experience like Cédric speaks about testing, you pay attention. And I did.

Among the very interesting quotes from Cédric that Andrew has reproduced, the following really struck me:
Another important point is that unit tests are a convenience for *you*, the developer, while functional tests are important for your *users*. When I have limited time, I always give priority to writing functional tests. Your duty is to your users, not to your test coverage tools.

You also bring up another interesting point: overtesting can lead to paralysis. I can imagine reaching a point where you don't want to modify your code because you will have too many tests to update (especially in dynamically typed languages, where you can't use tools that will automate this refactoring for you). The lesson here is to do your best so that your tests don't overlap.
Trust me, as a test-infected developer, I would love to stay in a state of self-delusion and pretend that test-induced paralysis doesn't exist. But that would be a lie: the reality is grimmer than the wonderland of testing I would wish to live in. The reality is that tests both encourage and resist change.

On the one hand, tests encourage and support refactoring: when the behavior of the application should not change but the code needs to be re-organized, tests are a blessing. They give you the courage to dare changing code because of the immediate feedback they give when you've been refactoring a little too aggressively. And this is priceless.

On the other hand, tests resist behavioral changes. Because tests have captured all the nitty-gritty of your application, when comes the time to change its behavior, you will need to invest time to adapt your tests accordingly, and this whether you rework the tests first or not. As Cédric pointed out, in a dynamically typed language, this is immensely painful as development tools are almost useless in assisting you with the required changes. Similarly, if you use mock objects, you are good for going down a deeper Circle of Hell, where more painful and frustrating manual fixes await you.

So, is there any hope out of this love / hate relationship? Knowing that "the only way to go fast is to go well" dumping tests altogether is certainly not an option. Could the solution lies in Cédric's very last words: "do your best so that your tests don't overlap"?

At this point, I don't know yet but I've decided that, as a starting point, I should start to estimate the amount of overlap I'm dealing with in the Erlang game server I'm working on. Interestingly, what I've found could pretty much apply to the vast majority of Java projects I've been previously working on. Maybe it applies to your projects too?

The first thing I've looked at is the testing overlap that exists between two layers of our application:

As you can see, the overlap exists because tests of the upper layer rely on mocks to simulate all the happy paths and most of the unhappy paths of the underlying layer. The overlap is not total because a layer tend to reduce the granularity of the unhappy paths it faces internally in order to expose the upper layer to a limited amount of bad situations to deal with. Hence the limited amount of mocked features in the overlap area.

When applied to a typical vertical slice of our system, it looks like this:


This is not too bad. Until the wind of feature change comes blowing on this mock-based card-house of tests, life is peachy.

Until now, the tests I have been looking at were only unit and database ones. If I add our functional tests on top of the overlap diagram, here is what I get:


Now the application container is also tested, plus we get an insane amount of overlap.

But the amount of overlap is not what I want to discuss first: it's the test coverage profile that I want to look at first. Notice how the functional tests explore less unhappy paths as they exercise deeper application layers. This can be explained simply: some unhappy paths are very hard to reproduce via the reduced set of functionalities exposed at the top level, oftentimes because they require a very specific and complex state to be established beforehand or conditions that could only be met in case of low level failures (loss of networking, for example).

It's obviously out of the question to consider dropping functional tests in order to reduce the testing overlap. As Cédric said, they are the only tests that have a true value for the end user of the system. My experience confirms that you can reach a nearly flawless first-time client integration if your functional tests have a coverage profile that is similar to the one in the last figure above.

The only problem lies in the quality of feedback you get from functional testing: because it's impossible to make the gory details of the errors encountered when exploring unhappy paths surface at the uppermost level, your system must have a solid logging strategy that allows you to precisely track issues, should you decide to code using functional tests as your only safety net.

So are the unit tests overlapped by the functional tests the ones that must go? Cédric again gives the answer: if time is short, it's better to focus on the functional tests. Of course, if you have a battery of unit tests in place, keep them.

But, maybe, just maybe, as you move to your next project, consider writing functional tests firsts? That way you would have built first the tests that truly matter and, if time permits, write unit tests as you implement the features expected by the functional tests.

Sunday, November 15, 2009

Zulu Zabbix

I am posting this mainly for the sake of reference and, maybe, helping others with the same problem.

If, like us, you're running the Zabbix monitoring platform in Zulu time (aka UTC), you should have noticed a time glitch when displaying historical graphs.

The cause of this problem is simple: the fancy controls in the browser-based user interface are rendered using JavaScript, hence based on the time of the machine used to browse the graphs.

Though we are strict in running all our servers in Zulu time, we haven't crossed the chasm and decided to run all our workstations and the rest of our life in UTC. So here is the simple fix you can apply to js/sbinit.js



The idea is to simply add the local browser time offset to the Unix time. With this fix in place, you will enjoy good looking graphs and correct navigation in them.

Time is really the stumbling block of software engineering...

Wednesday, November 11, 2009

Meeedia Playeeer

I've been caressing the idea to buy a Wi-Fi enabled media player in order to tap into the gigabytes of (legal) music that sits in my NAS. I've considered investing into a Logitech Squeezebox, or a similar product, but I wasn't sure such a device would be able to play directly from an NFS share, without any music server running somewhere.

Just when I started to consider building a player out of a SheevaPlug, I remembered of the ultimate source of cheap hardware, ready to be repurposed: eBay. $125 and a few days later I had a like-new black Asus Eee PC 2G Surf waiting to be turned into a music player.
The unit came with Ubuntu Eee Hardy Heron on it and only 50MB of free space left on its 2GB solid state drive. After a merciless review of all the installed applications, I ended up with 200 MB of free space, ready to host a music player.


Finding the right music player was no small feat.

I really enjoy Audacious on my work laptop because it's plain simple and is able to play music directly from an NFS mount without any glitch. But it lacks an integrated library manager, which is a must for any software powering a machine dedicated to playing music.

So I went on trying all the players with integrated music library manager I could find in the Heron standard software repository (I won't quote names because most of these applications have now better versions available). All of them were suffering from multiple woes rooted in their bad handling of network fluctuations. The most common issue was a too short not-configurable music buffer, leading to broken music replay. The worst issue was with a library manager that was not only taking ages to scan my 16+GB of music but also, on the first network glitch, would start to delete songs, one by one, from the partial library it had created (talk about defensive programming gone bad).

So I ended up installing Amarok. The reason why I didn't immediately install it, knowing it has been my favorite player for all the time I was on Kubuntu (until the KDE 4 debacle), is its sheer size. It's a 120 MB install and on an almost full drive it didn't feel like a good idea to try it first.

This turned out to be the perfect match! Not only Amarok plays music from my NFS mount without a glitch, but its music library is totally unaffected by disturbance in the Wi-Fi signal.

All in all, my Eee Music Player is doing great. It only takes a few seconds to be resurrected after being suspended and music starts playing soon after.

Do you think repurposing full fledged computers into single application hosts is a crazy idea? Is it something you've considered or done already?

Saturday, October 31, 2009

Software Manifestos: A Matter Of Trust?

As software manifestos have started to proliferate these past months, I have started to wonder what could be the root cause for their creation. Why would thought leaders gather, assert a small set of values and shrink-wrap them as a manifesto, calling for others to sign it? My feeling is that these manifestos are the expression of a pushback on a particular aspect of software development that went insane.

Here is a little game: match the manifestos with the software insanities they push back on:

Big methodology and design up-front
Software craftsmanship manifesto
Army of flying monkeys testing

Agile manifesto
Snake-oil vendors and ivory tower architecture
QA manifesto
Reckless programmers and incompetent coders
SOA manifesto

(One manifesto I see missing here is the "recruiter manifesto", which should push back on inane keyword-driven head hunting schemes solely able to put the wrong people at the wrong spots)

If we dig deeper, we become tempted to ask why is our industry suffering from such insanities? What does make software different? Could it be because of complexity?

Complexity. Software entities are more complex for their size than perhaps any other human construct because no two parts are alike (at least above the statement level). If they are, we make the two similar parts into a subroutine--open or closed. In this respect, software systems differ profoundly from computers, buildings, or automobiles, where repeated elements abound.
Frederick P. Brooks, Jr., No Silver Bullet

The natural reaction to complexity is to try to escape it at all cost, even if it means wilfully practising self-deception. Hence silver bullets, hence snake oil vendors, hence all these methodologies, governance committees and ivory towers that are there to nurse the insecurity of higher levels of management by giving them the impression software creation is under control and, finally, out of the hands of programmers.

Of course, it doesn't work that way: years and millions of dollars later, reality comes knocking at the door, manifestos are getting written and everyone is sent back to the same fundamental question they've been trying so hard to avoid: how to build trust in software developers?

And that's of course a question for us, software developers. How can we build such a trust in us when so many forces are pushing towards the opposite?

Granted that software development is unpredictably complex and that this complexity reveals itself when the devil shows up (those pesky details), it is clear that the overall battle of trust is fought during each decision, when tackling each detail and writing each line of code.

I think we could learn a few lessons from the world of aviation, where trust in pilots has been built progressively and methodically. When you fly an airplane, you have plenty of decisions to make and losing any of these battles can end up very badly for everyone. So why are pilots trusted? Aren't they fully superseded by ATC anyway? Answer is no: even if ATC has authority, the PIC (Pilot In Command) has the last word because he is the one out there dealing with the ultimate reality of flight. Despite its authority, ATC doesn't micro manage the pilot: the pilot is in-command.

To have the privilege to be a PIC, you have to remain current and regularly prove that you can be trusted for your judgement based on your skills, experience and training.

If the acronyms didn't sound so bad, I would dare suggesting programmers should become DICs, ie Developers In Command. Though working under different forms of authority, DICs would be fully trusted for taking the final decisions in the daily battle of writing code. In this world, it wouldn't be an heresy to say that developers could build large and complex software systems from the ground up, without the need for snake oil, committees or big design.

When trust will be manifested, we won't need manifestos anymore.

Monday, September 07, 2009

Looking for my seams

Like any test infected programmer switching to a new development platform, I have spent my first days working with Erlang looking for my seams. Here, I am talking about seams as defined by Michael Feathers in Working Effectively with Legacy Code: "A seam is a place where you can alter behavior in your program without editing in that place." As such, seams are key enablers for unit testing as they allow you to redirect calls leading out of your SUT to mocks or stubs or any kind of test double you tend to favor.

In object oriented programming, this is a given thanks to polymorphism and dependency injection. But in Erlang, where SUTs are MUTs (modules under test) and the common idiom for invoking a method is module:function(parameters), things are a little less obvious. Indeed, hard-wired function calls from one module to another don't leave much room for any kind of substitution. Without the capacity to fully test my modules in independence, I quickly started to feel uneasy. After a few days, it felt like free-falling without a parachute.

Then I started to seriously investigate my options...


Macros

Macros allow you to define blocks of instructions that the pre-compiler will substitute for you at the different places you refer them. When used in conjunction with flow control statements, macros can be used to switch one code fragment with another one by passing a parameter to the compiler. This seems to fit the bill as you can use conditional macros to alter behavior without editing the places where the macro is used.

This said, I have quickly ruled out the use of macros as a valid seam. Imagine having to do this for all the function calls leading out of the MUT:

Moreover, if a mistake exists in the non-unit test wiring part of the conditional macro, I would have had to wait for integration tests or actual deployment to get feedback on the issue.


Funs

Though the common idiom is to early bind the module and function you want to call, Erlang is fully capable of late binding and dynamic invocation, as this very crude example illustrate:

This opens interesting possibilities for MUTs that expose higher order functions. If the function that must be tested accepts one or several functions, passing a mock implementation is just a matter of providing an anonymous function of the same arity. This mock would perform nothing besides storing the received parameters in a shared storage, like the process dictionary, for later inspection.

Unfortunately, not all functions receive their dependencies as parameters but instead perform direct calls to other functions in other modules. It could be a plausible and drastic design decision to forbid all direct inter-module calls in favor of passing dependencies as anonymous functions via additional arguments. Some have suggested to use a record to pass around all your application dependencies as a single extra argument added to all functions.

Interesting but the idea of polluting all functions with additional arguments is less than palatable. In fact, it would great if these extra arguments could be defined module-wise and implicitly added to each of its functions... Rejoice! Parameterized modules have been introduced to perform exactly this delicious syntactic sugar trick!


Parameterized modules

I have discovered parameterized modules while writing controllers for Mochiweb. In this pretty cool HTTP server, the request reference that your processing function receives points to a parameterized module, allowing this kind of neat syntax:

Though this may feel object oriented, don't get fooled: behind the scene, there is no instance of anything. The Request reference contains all the hidden parameters that the get function needs besides the atom specifying what you want to get. Behind the scene, what really happens is more likely something like that:

But because the Mochiweb Request is a parameterized module, all the extra parameters have been specified once, packed in the reference and stay hidden there for your utmost convenience!

From there, it's easy to see how to write stubs for parameterized modules: just write another parameterized module that export functions with the same signature as the ones you use in the real module. Here is a very incomplete but fully working request stub for Mochiweb:

Note how I use the process dictionary to store values that I will later retrieve for asserting everything went as expected. By using parameterized modules, I have been able to reach near 100% code coverage. Does this mean parameterized modules are the best thing since sliced bread?

Well, so much for the free lunch as there are some drawbacks to consider:
  • Experimental - Parameterized modules are still officially considered as an experimental feature of Erlang, hence subject to change. Unlike the Java world where everything is kept for ever just in case, Erlang doesn't patronize developers, so if this feature is one day bound to oblivion, it will be tossed out. And quickly.
  • Unchecked - Unlike with a direct module's function reference, compile-time checking is not available, leading to possible bad surprises at runtime. If the parameterized module reference your code uses does not expose the expected function, you're in for a nasty error. In fact, you can totally pass a reference to a Foo module while your function expects a totally unrelated Bar module. As a tentative mitigation, I have added a verification function in my modules so they ensure at start-up time they are correctly wired. This feels like framework-envy,so I'm not fully satisfied with this approach.
  • Confusing - Because the actual module is not directly referred to, reading such code becomes more complicated. You have to infer from the context (or some coding conventions, or even comments) what is the module that will actually be wired-in at runtime. Decreasing understandability is definitively not a good thing.

Besides these downsides, I still believe that the complete MUT isolation and behavior swapping facilities offered by parameterized modules make them a very interesting tool for the test-minded Erlang developer.


Closing notes

MUTs have other kinds of dependencies that you will want to substitute at unit testing time. To name a few:
  • Process dependencies - A MUT can contain functions that directly depend on other processes via their PIDs (process IDs). An interesting seam here is the local registry of processes (and ports) that you can use to set-up test processes and register them under the same name as the ones used at runtime.
  • Mnesia - Stubbing out calls from the controllers to the DAO is a good strategy but what about the DAO itself? Instead of stubbing out each Mnesia call, I have opted for running it in-memory at unit test time (à la hsqlddb) and activating file persistence only at runtime. This is extremely fast so very well suited for the task.

Finally, if you wonder what unit testing framework I am using, I have opted for etap, which I find very simple and powerful enough for my needs. If you want something more structured and feature-rich, EUnit is the answer.

Free fall is over: I have found my seams and landed seamlessly. Please share your own test infected adventures in Erlang.


UPDATE 23-SEP-2009: Hot code swapping is also a very powerful seam, that has been smartly leveraged to create ErlyMock, a quite capable mock framework for Erlang.