Optimize Database Performance in Ruby on Rails and ActiveRecord
blog.appsignal.com
blog.appsignal.com
If you want to reuse the code between these objects and the corresponding ActiveRecord model, do it with a module.
The biggest problem with Rails is that people get so glued to the conventions that everything gets shoehorned into a model or controller instead of using a better abstraction.
As a Rails fanboy, I couldn't agree more.
Rails' semi-insistence on doing everything convention based and rewarding you for it can be a double edged sword in that way. The idea of doing anything outside the bounds of a controller/model/job sounds scary, especially when you consider how flexible Ruby itself is and how off the rails (heh) you can go pretty quickly. But at the end of the day, we should not be afraid of creating new conventions - which is a demonstrably different idea than just devolving into a raw Ruby scripting bonanza.
The conceptual compression of having activerecord be a database table mapper, entity class, form/input validation, and query builder, it's both its greatest strength and its greatest weakness, depending of the stage of the business. FWIW the ruby community has more suitable alternatives which self serve the kind of abstractions you describe, such as hanami.
Sometimes you just need SQL not everything that AR gives you. Most apps are read heavy, so you need an escape hatch. For me, Arel was always that: a programmatic way to alter SQL.
Hanami is a complete Rails replacement. I wouldn’t advocate for that at all.
> Sometimes you just need SQL not everything that AR gives you.
I used to agree, until I took a step back and realised how much ruby code ceremony I had to write in order to write, p.ex. A multi insert statement With an expression. It was several character more, and didn't seamlessly trabslate to SQL. I eventually replaced the AREL craft with core sequel, where I'd use it to build the queries and pass the statements to the AR sql execution method. I could have gone with raw sequel too.
> Hanami is a complete Rails replacement. I wouldn’t advocate for that at all.
The top level comment mentioned abstractions that something like hanami already supports. Instead of bending rails, to something it was not built to support, you either pick an alternative tool that does, or you accept what rails does and move on.
https://speakerdeck.com/cgriego/models-models-every-where?sl...
If I see a materialized view I’d definitely go back to see if I could eke out any other optimisation in a straight view rather than use it.
With regard to Scenic, I like the way it separates out SQL but I’ve always found how it versions view files with an iteration in the file name a pain.
I wish it had the option to just pick up changes in the source file without needing a migration to bump that version number.
On larger projects it’s easy to run into version number clashes in different pull requests.
Now I'm on the JVM and Hibernate is the equivalent of AR there: an ORM.
We try to get rid of Hibernate were we can: it is slow, comes with huuuuuge conceptual overhead (detachment/lifecycle/lazy/etc), makes it easy to write bad code (e.g. queries in loops), and you still need to know SQL (for complex queries that dont fit in the ORM).
So we use jOOQ now. Much better. Somthing that's AR also offers (but without the type safety). LINQ also has a similar interface (the one w/o the fancy syntax).
Alternatively there are tools like SQLDelight (Kotlin) and Sqlx (Rust). That provide an alternative path to "just use SQL" without resorting to queries-in-strings.
As you write, jOOQ and similar are much better approaches. For Ruby, Sequel is a better library than AR for a number of reasons.
Here’s a related post I wrote for AppSignal:
What's Coming in Ruby on Rails 7.2: Database Features in Active Record https://blog.appsignal.com/2024/07/24/whats-coming-in-ruby-o...
For folks interested in additional depth on optimizing Postgres for use with Active Record/Rails, please check out my book:
High Performance PostgreSQL for Rails https://andyatkinson.com/pgrailsbook
Thanks!
It feels like I haven't heard anything new about Rails for the past five years, but suddenly in the last few weeks it's been a trending topic on HN.
The changes coming with Rails 8 are really good for small teams working to tight budgets.
The new deployment strategy is amazing compared to some of the complex react, micro services, k8s and cloud stacks I’ve worked with.
They always involve a lot of engineering effort and a lot of expensive proprietary services.
If you want to get off the ground quick for little money a Rails monolith with the frontend using Hotwire still looks very hard to beat.
If Shopify, GitHub, etc can scale to be the largest vendors in their respective markets you know you can too.
Upsides: hilariously easy deployment and development story. Migrations and secrets management is trivial. Everything works. No long searches for third party dependencies.
Downsides: speed (as in compared to a Java backend, for example), and activerecord has a learning curve on performance. Did some bad design decisions on two endpoints that my iOS client depends on and those design decisions hurt badly. They would’ve hurt in any language but hit me much much sooner with Rails.
I've seen a ton of different "we're switching to Rails" posts floating around like these -
* https://www.reddit.com/r/rails/comments/1dkcegr/im_switching...
* https://www.reddit.com/r/rails/comments/1ah1b36/is_the_rails...
There's also the new Rails Foundation - https://rubyonrails.org/foundation and their new annual conference, Rails World, which has proved pretty popular - https://www.reddit.com/r/rails/comments/1fqizim/rails_world_...
So, Rails ain't dead yet :)
Like other commenters have said, the Hotwire stack plus the new “solid” stack of database adapters is really cool. The general ethos of simplifying the web stack and “compressing the complexity” as DHH says is really appealing.
I’m migrating a personal project from python/svelte and it’s been a ton of fun so far.
I would say that it's popularity has definitely waned in the last 10-15 years, probably in part due to Javascript gaining more ground, perceived performance problems with Ruby and the fact that many frameworks in other languages adopted some of Rails's best features (such as convention over configuration, DB migrations, unified project structures, etc.) and ignored its worst parts (such as monkeypatching).
You can check out the source here: https://github.com/freeats/freeats
There's the not-so-common case of needing database independence as well, but at that point DB perf becomes a really hard problem to solve generally...
Sql is awesome and gives immediate access to the full raw power of your database.
It’s worth learning whilst learning some abstraction isn’t.
SQL is ultimately a bit dysfunctional and takes a lot of getting used to. Not arguing it's worth learning but since most of my career I was not truly exposed to it, I preferred the bespoke DSL.
I'll agree using ORMs is indeed going to ridiculous lengths to avoid learning even a smidgen of SQL but there's a nice middle ground.
no, this is misleading. There are different kinds of objects. if you use JDBC to run a query you get back something like a Row object (sorry I havent done JDBC since the 1990s) - the "Row" like object does not declaratively define the fields of the row, the fields and the data of each field are all data. however when you get back POJOs, as you say, these declaratively define the fields that are "mapped" to a column. if jOOQ does this, it's an ORM. ORM has nothing to do with writing SQL - that's called a "SQL builder". the ORM is about marshalling data from POJO-style objects to and from relational database rows.
relational
mapping
ORMs map _objects_ to _relations_ (i.e. tables).
"Unlike ORM frameworks, MyBatis does not map Java objects to database tables but Java methods to SQL statements." https://en.wikipedia.org/wiki/MyBatis
"Jdbi is not an ORM. It is a convenience library to make Java database operations simpler and more pleasant to program than raw JDBC." https://jdbi.org/
"While jOOQ is not a full fledged ORM (as in an object graph persistence framework), there is still some convenience available to avoid hand-writing boring SQL for every day CRUD. That's the UpdatableRecord API [which is only one part of it and you don't have to use it]" https://blog.jooq.org/how-to-use-jooqs-updatablerecord-for-c...
With something like jOOQ, the query language is basically SQL, just made type-safe. You write a query that maps to an SQL query 1:1 and then you map the results to whatever you need. No implicit saving, auto-loading of associations in the background etc.
So it's not about "people should use the SQL syntax instead of query builders", it's "people should write relational queries explicitly instead of relying on the framework to save and load stuff from the database when it deems it necessary". Your domain objects do not need to know that they're persisted and they don't need to carry a DB connection around at all times (looking at you, ActiveRecord).
ActiveRecord also does a terrible job of hiding the database or sql, even compared to other ORMs like Django's.
So the price you pay for the it doesn't even buy you that much benefit via abstraction in the first place.
I currently spend a large proportion of my time working in a Java code base that uses JDBC directly. There are many places where the complexity of the work to be done means code is being used to assemble the final SQL query based on conditionals and then the same conditional structure must be used to bind parameter values. Yes, in some places there are entire SQL statements as String literals, but that only really works for simple scenarios. There are also many bits of code that wrap up common query patterns, reimplementing some of what an ORM might bring.
I recently implemented soft deletion for one of the main entities in the system, and having to review every query for the table involved to see whether it needed the deleted_at field adding to the where clause took a significant amount of time. I think better architecture supported by a more structured query builder would have made this much easier. For me that’s the main benefit of an ORM.
What's not to understand? ActiveRecord exists to make queries easier to write and more readable - the same reason any library or tool is created.
Why don't we skip using tools like Ruby or Rails and just write everything in machine code instead? /s
It achieves only making queries much harder and much more opaque. It completely fails at that.
Plenty of companies end up building the wrong product altogether and need to pivot when they realize customers don't care about feature X and need feature Y instead. In cases like these you hope you get the right product out there and survive long enough to regret building with the fast framework instead of the finely tuned ultra performant version.
Like any advanced tool, it’s possible to shoot yourself in the foot if you don’t use it correctly. But once you know it, it makes simple queries easy, complex queries tolerable, and allows progressively dropping to raw SQL if needed.
I’d go even further and say ActiveRecord (particularly when using Arel for complex query construction) is the single best ORM I’ve ever used. I miss it hard whenever I interact with an SQL database in any other language.
Not about using raw SQL when needed. I would definitely do that for a hot, complex query if I felt I needed to but I think ActiveRecord is rock solid if you know how to use it and how to examine its output.
There are times where an ORM is a very useful tool and I've found ActiveRecord to be better than most home made query generators I’ve run into regardless of base language.
This comes with the caveat that someone will have to work with bespoke-sql-codegen:0.0.1 when you go work in another company, with StackOverflow being of no help, as well as an unknown amount of vulnerabilities.
I once worked on a Java project that put like 90% of the logic in the DB and the application mostly called prepared statements and parsed the results with a bunch of low level logic. There was some dynamic SQL generation but the end result was the whole thing feeling like code archaeology because of course there was basically no documentation or good examples on how to do things, decoupled from the already complex codebase.
It was blazing fast but also an absolute nightmare to work with. I would be similarly guarded towards using anything bespoke without a community and knowledge base, regardless of the stack.
As for ORMs, there’s nothing preventing you from making an efficient DB view and mapping that into an entity.
Activerecord may not give optimal solutions but it can get close enough for a lot of workloads, and complicated sql can become a complete bear over time.
ActiveRecord was specifically designed to work in that way—tools for most queries, which are simple, and making it easy to use SQL for more complicated cases.