Rails is a framework well known for ease of development. This ease is achieved by a lot of ‘magic’ that happens behind the scenes. One of pitfalls of such magic is a false sense of safety it gives, including sense of safety from concurrency issues for single-threaded environments. You may never discover any problems before the launch, or even after, while your site traffic is pretty sparse. But here comes a glorious moment of popularity - and together with more traffic it brings more and more concurrency-related problems.
In this talk we will look at different aspects of concurrency, from simple ones that are even mentioned in Rails documentation, to more complex problems that even seasoned developers tend to miss or fail to pay sufficient attention to.
Good presentations, deserve watching:
- Deconstructing the Framework by Gary Bernhardt
- Agile is the new black by Fred George
- Confident Code by Avdi Grimm
- Delicious Controversy: Docs & Tests by Thomas Meeks
- Boundaries by Gary Bernhardt
Today I was browsing Rails codebase (sometimes people do weird things for fun), and found an interesting piece of code:
This sparkled my interest: what is divmod, especially - its pretty harsh form divmod(1)? Quick search through documentation - and it starts to make perfect sense:
Returns an array containing the quotient and modulus obtained by dividing num by numeric; especially if you instantly remember what is
modulus. Looks like example table in documentation is designed to confuse (including expected output of multiple methods), but for the special case of a positive float.divmod(1) there is much simpler explanation: it just splits a positive floating point number to its integer and fractional parts (+/- error).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
What are we doing here? We create two records, touching first (this updates the record timestamps), and making sure that the first record is not the same as the last one. Perfectly valid assumption, that almost works.
The test succeeds on SQLite and MySQL (latter - with various engines), but fails on PostgreSQL.