Another excellent day in Chicago. Today I got to meet the CEO and founder of the current client. Our introduction was rather brief and, as one would expect, we got straight to business talking about the details of the first feature we are making for them.
One of the more interesting issues tackled today was a race condition where several users are trying to access the same expiring option at the same time. Say we have this sweepstakes going on, and only 10 people get to sign up for. This sweepstakes is released on twitter, and immediately 50 people apply to sign up, all at virtually the same time. You have a database which holds only ten redeemable account numbers, but this database is being accessed by several servers at the same time! On the very last redeemable account, 5 people request the server at such a close interval that each of their servers request the database for the same account at the same time. You can say that the last account should go to the first request for it, but what these requests are being done in parallel, so the first request in might be the last request out. Who gets it? How do you check too see if there is still an account remaining while other servers are grabbing the last account?
Turns out that Ruby with MySQL has a very nice lock! method that can be called in a model to guarantee only one write can be made to a certain row in the database at any given time. It does something else, but let me query you first. Is that enough?
Even if only one of these 5 servers was able to write to this database row, thus editing it to grabbing the last account, the 4 other servers could still read the row and therefore determine (if reading just before the row was being edited, but after it was locked) there is still an account left. The remaining servers might then try to edit the row the moment the lock was released, which could cause some issues.
Its not enough to simply lock the writing of this row, but you also must lock it so that nothing else can be reading it while it is being written to. In this way, the first of the 5 servers would lock the row, at which point the other 4 servers would have to wait until the 5th server officially had the last account before they could read the row.
Fortunately this is what the lock! method does, when using the LOCK IN SHARE MODE. This along with using a Ruby transaction (which is an all or nothing access for an object), quite nicely resolved this race condition.
Micah wrote up a script to test this (because unfortunately rspec performs its tests using transactions rather than actually accessing the database, thus making testing this race condition virtually impossible in the typical form) which would create 5 Ruby forks (not threads since Ruby threads all run through the same interpreter, and thus are really actually just one thread [ Not in JRuby though!!]) which would all request the last redeemable account at the same time. We confirmed that it worked by seeing a couple of forks grabbing the last account while only using Ruby transactions, and then confirmed the lock! worked by running this script again and again. Micah actually did a cute little command to run the script 30 times, and it never failed even through that. Though it seems nearly impossible to be certain that it worked, it seems quite likely that it works.
Start reading the Agile Web Development With Rails - Third Edition, and got about 30 pages in tonight. I am really glad I started reading this after creating my Socket Server because not only does it solidify my server knowledge, it also gives me a new realm of connections to make so that I better understand how Rails really works.
A quick overview of what I have gone through so far.
Chapter 1 was mostly all intros and talk about how Rails have developed and how this book will be structured, so never mind that. Chapter 2 is where the details begin.
Chapter 2 revolves around explaining MVC.
MVC is of course the Model View Controller framework that Rails, along with many many other applications, uses.
MVC - Model View Controller
Model - handles all data, data interaction, and business rules. Gate keeper and data store.
View - UI, normally based on model. Never handles incoming data. Different views for same model.
Controllers - The corner stone of application. Receive requests, handles them using the models, display results using views.
store - name of controller
add_to_cart - name of action
123 - extracted into id
The controller find's the user shopping cart and product (model)
Tells the cart to add the product
Shows updated info to user
Organizing data (ObjectOriented vs relation databases):
Database-centric - Bringing data and database modifications directly into production code. Ewwwwww
Object-Relational Mapping - Uses OO to control data. Maps Classes to tables, objects to rows, and columns to attributes.
Active Record - Follows the ORM model.
Often just batches of HTML, but typically wish to have some actions. Actions provided by action methods in the controllers.
Dynamic content done in 3 ways using templates:
ERb (embedded ruby)
Responsibilities - Routing external requests (friendly URLs), Managing Caching, Managing helper modules (for views), Managing sessions
Certainly a quick summary of the chapter, but if you want to know more details, the book itself it a pretty smooth read while retaining high content.