This post is about comparing the feature and performance between Elixir's Ecto library - database wrapper and languge integrated query for Elixir and Ruby on Rails Active Record.
Round 1: Database Migration
Active Record
In Ruby on Rails Active Record, we can generate a migration for a model by following these steps:
Step 1: Generate model file and migration file
1
|
|
And the content of the migration file is as following
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Step 2: Create the database
1
|
|
Step 3: Run the migration
1
|
|
That's all for Active Record
Ecto
Assuming that you have already put in ecto
and postgrex
dependencies and already run mix deps.get
, here are the steps:
Step 1: Create Repo file by manually adding web/models/repo.ex file witht the following content
1 2 3 4 5 6 7 8 9 10 11 |
|
Step 2: Create the Postgres Database
1
|
|
Step 3: Create model file web/models/book.ex
with the following code:
1 2 3 4 5 6 7 8 9 10 |
|
Step 4: Generate a migration file from terminal
1
|
|
Step 5: Edit the newly generated migration file
There is a need a type the SQL to create the table here:
1
|
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Step 6: Run the migration file
1
|
|
Step 7: Add BookStoreElixir.Repo
as a worker in line 11 of lib/book_store_elixir.ex
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Conclusion for Database Migration:
Active Record wins as there are fewer number of steps (3 steps in Active Record vs 7 steps in Ecto). Ecto will need to have more mix tasks in order to be more on par with Active Record.
Round 2: Database CRUD (Create, Read, Update, Delete) operations Syntax
1 - Create
Active Record
To create a new record in Active Record, we will use create
command on Book
model
1 2 3 4 5 6 |
|
Ecto
To create a new record in Ecto, we will use insert
command on Book Repo
1 2 3 4 5 6 7 8 |
|
Syntax wise, they are quite similar. Although Active Record code seems to be a bit neater but it can be considered a draw for creation syntax.
2 - Read
Active Record
In Active Record, you can perform quite an entensive list of queries by using where
, order
, limit
, offset
, group
, join
, having
, select
, pluck
, includes
. For instance
1 2 3 4 5 6 7 8 |
|
Pretty amazing that Active Record can do chaining in combine into 1 single query.
Ecto
There are 2 types of writing Query in Ecto
- First Way: Using query syntax provide by Ecto
1 2 3 4 5 6 7 8 9 10 11 |
|
- Second Way: Using chaining by utilising Elixir Pipe will make query just like Active Record:
1 2 3 4 5 6 7 8 9 10 11 |
|
Syntax wise, both Ecto and Active Record have shown that they are quite expressive in showing the data transformation through a series of query functions. However, Active Record syntax is still considered better in this Round as it is showing an easier approach. Although, the second way of query that we use for Elixir Ecto is pretty closed to Active Record syntax, it still exhibits a more wordy syntax than Active Record. Albeit, Elixir Ecto is using a nice approach by letting programmer to have access to a "so called" object instance in the query - having[book, book.id >= 1)
vs "having("books.id >= 1")
3 - Update
Active Record
To update a record, we will use update_attributes
on Book
instance
1 2 |
|
Ecto
To update a record, we will use `update
on BookStoreElixir.Repo
1 2 3 4 |
|
Both Ecto and Active Record have exhibited almost the same level of code when updating a record. Hence, it is a draw.
4 - Delete
Active Record
To delete a record, we will use destroy
on a Book
instance
1 2 |
|
Ecto
To delete a record, we will use delete
on BookStoreElixir.repo
1 2 3 4 |
|
Both Ecto and Active Record have exhibited almost the same level of code when delete a record. Hence, it is a draw.
So for this Round 2, Active Record is the winner (1 vs 0).
Round 3: Speed of Creation
Note that this is tested on my local machine. The result here is just for your reference, and should not be treated as an official benchmark. I am using Ruby 2.1.2, running on Rails 4.1.4 and Elixir 1.0.0. Both Rails and Elixir application are running with Postgres 9.3.5. For Rails, environment in Rails 4.1.4 has been preloaded with Spring, hence there is a not a need for system to spend time to load the Rails environment. Further more, the Rails code was run with the log mode the same as on production - i.e. no query log was printed out.
Active Record
Here is the code inside db/seeds.rb
1 2 3 4 5 6 7 8 |
|
Here is how I run it:
1
|
|
Here are the results of 3 running rounds (with creating 100, 10,000 records and 1 million records). Each time database drop, creation and migration were performed before hand.
1 2 3 4 5 6 7 8 9 10 11 |
|
Active Record took 1.706 seconds, 12.328 seconds and a whooping 17 minutes 52.272 seconds to complete 100, 10,000 and 1 million record creation run.
Ecto
I create a file seeds.ex inside the elixir project folder with the following content
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Then run this seeds
1
|
|
And here is the reusult of 3 runs:
1 2 3 4 5 6 7 8 9 10 11 |
|
Ecto took merely 0.624 seconds, 5.109 seconds and 6 minutes 15.161 seconds to complete the same 3 runs of creation 100, 10,000 and 1 million records.
Here is the chart that shows the performance of Ecto vs Active Record through 3 runs:
Ecto is approximately 62% faster than Active Record. Hence, Ecto is the winner in this round.
Round 4: Speed of Updating
We will now run updating on 100, 10,000 and 1 million records in each database each. We will update the records, one by one to have a new book title. As usual, let's start with Active Record first
Active Record
Now in my db/seeds.rb
file, I will change it like following:
1 2 3 |
|
And run this file:
1
|
|
1 2 3 4 5 6 7 8 9 10 11 |
|
Active Record took 1.725 seconds, 12.414 seconds and 23 minutes, 30.364 seconds to update all 100, 10,000 and 1 million records respectively.
Ecto
We will change the seeds.ex
file to following
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Before you can run this file, you will need to do a bit modification on postgrex
library as this library is throwing a time out if a query spends more than 5 seconds to complete. Repo.all(Book)
will definitely take up more than 5 seconds to load up the entire collection of books into the memory
From the project folder, what I did was edit file deps/ecto/lib/ecto/adapters/postgres.ex
1
|
|
Then search for @timeout 5000
and replace by @timeout :infinity
Now you can run and time the running time of this file
1
|
|
Here are the result of 3 runs:
1 2 3 4 5 6 7 8 9 10 11 |
|
Ecto took merely 0.606 seconds, 5.495 seconds and 7 minutes, 12 seconds to update 100, 10,000 records and 1 million records respectively. Apparently, Ecto is the winner.
Here is the chart showing the above elapsed time:
Conclusion
With the fact that Active Record and Ecto each has 2 wons, it is concluded to be a draw between Active Record and Ecto. Ecto seems to be very promising library, which provides a very interesting way to write a database query. The steps to setup and the syntax of Ecto will definitely improve in the future.