• The REPL: Issue 83 - July 2021


    I recently found GoodJob, a background-job library for Ruby. It’s compatible with ActiveJob. Its main selling points is that it takes advantage of Postgres features. For projects already on Postgres this means two things: There is no need for another data store, and job scheduling can be transactional with the rest of the application.

    Sidekiq, the leading background job library requires the use of Redis. It scales exceptionally well. For many applications managing another data store in production is burdensome, and provides little tangible benefits, especially if the load on the database is low. GoodJob even has a mode that runs the workers in the same process as the Rails server. For smaller apps running on Heroku, this can remove the need of a separate dyno.

    Regarding the transactional nature: Suppose you want to store a record and queue a background job as part of some business operation. If you write to your main database first you run the risk of failing when enqueuing the job. Enqueuing inside a transaction doesn’t work either. In case of a transaction rollback, the job will still be published, like Brandur explains. Keeping jobs in the same database as the rest of the data, allows for transactional semantics – much easier to code against.

    I’ve only tried this library on a side project with little traffic, but so far I am very impressed.

    Idempotency-Key IETF Standards Draft

    Speaking of Brandur: He notes that the IETF has a new draft standard for an Idempotency-Key, already in use at Stripe and other places. He previously explains in more detail why it’s important.

    Read on →

  • Per-project Postgres with asdf and direnv

    My development computer typically has a number of different projects, each needing specific versions of some tools. Previously, I wrote about using asdf to manage my ruby, elixir, crystal and erlang version. I’ve been using it successfully to manage Postgres versions as well.

    What I get

    • Precisely manage the Postgres version (e.g. 10.14, 12.5) for each project.
    • The Postgres data directory lives alongside other project files
    • No collision between projects. Each can have their own Postgres server running simultaneously.

    Read on →

  • The REPL: Issue 82 - June 2021

    EXPLAIN ANALYZE in PostgreSQL and how to interpret it

    I’ve been working on web applications for a long time. From time to time I am called upon to figure out (and optimize) performance issues on some query. EXPLAIN ANALYZE lets you see what Postgres is doing under the hood. I don’t do this often enough to remember all the detail of what all the information returned means. This article explains the basics of EXPLAIN ANALYZE and links to some handy tools that help you focus on what matters.

    Do You Need Redis? PostgreSQL Does Queuing, Locking, & Pub/Sub

    It’s no secret that I like Postgres a lot. This article explains some common use cases for Redis, and how Postgres can take care of them instead. It resonates with me, especially for smaller projects were the techniques outlined can avoid using another data store.

    Stop worrying about PostgreSQL locks in your Rails migrations

    To round off today’s Postgres love, this blog post introduces how the safe-pg-migrations gem can help when running migrations for databases with high-load and/or a lot of data. Traditionally, those operations can be problematic because of how Postgres acquires locks and can bring production sites down. The specifics are explained in detail in the post. The safe-pg-migrations gem manages to turn what would be complicated steps to ensure safe migrations into the same exact semantics that ActiveRecord::Migration uses.

    As soon as I tried this gem, I ran into an issue. The maintainer was very helpful. It turns out Ruby 3.0 support was pending. A few days later my issue was resolved.

    Read on →

  • Better Form Objects in Rails

    ActiveModel was introduced in Rails with the promise of being able to create model classes that interact well with the rest of the Rails ecosystem (e.g. routing, forms), but are not backed by a database table.

    It has some shortcomings, which are addressed by the active_type. Let’s look at an example.

    Read on →

  • Book Review: Grokking Simplicity

    Grokking Simplicity

    Taming complex software with functional thinking

    by Eric Normand

    This book explores functional programming in a way that is very approachable for those starting out on that discipline, yet is still worthwhile for people that are already somewhat familiar with functional paradigms.

    In Part 1, Normand explores the the difference between data, calculations and actions. Calculations are more traditionally called pure functions: They only depend on the inputs, and produce the same results when run multiple times. Actions are more traditionally called impure functions: The produce side-effects, and their results depend on where they were called. This key distinction is what the rest of the book is based on.

    Part 2 explores functional abstractions for iteration, map, reduce, filter, and how to work with nested data structures. It then it goes on to derive several concurrency primitives and apply them. Concurrency analysis can be quite tricky, but was made very approachable by the use of timeline diagrams.

    Overall, I though this book was worth it. The examples where concise enough to understand, but not trivial. The code in the book is written in Javascript, a language a I don’t write or know very well: It was not a problem. In fact, it gave me an opportunity to reflect on what affordances Ruby provides that are very functional in nature.

    I thought it was very neat that the author cautions the reader about the pitfalls of over-enthusiasm for newly acquired skills, and provides guidance on how to avoid it. There is also plenty of ideas on how to continue the functional journey. The last chapter even lists out what the author thinks the biggest takeaways are:

    1. There are often calculations hidden in your actions
    2. Higher-order functions can reach new heights of abstraction
    3. You can control the temporal semantics of your code


    Read on →