[Ruby ORM] Integrate Sequel to Ruby on Rails
Without any doubt, Ruby on Rails's still the hotest open source framework for web development. However, it doesn't mean RoR's perfect girl that all of guys wanna to date with. TeachEmpower has done a benchmark about web framework performance then RoR's position was the bottom of table. As a ...
Without any doubt, Ruby on Rails's still the hotest open source framework for web development. However, it doesn't mean RoR's perfect girl that all of guys wanna to date with. TeachEmpower has done a benchmark about web framework performance then RoR's position was the bottom of table. As a Rails lover, I want to figure out why Rails speed's so slow and find out the solution for this dark side. After some days experienced with Sequel, I has found the light at the end of tunnel.
There're so many article pointed out that ActiveRecord is one of reason contribute to poor performance of Rails. Someone has blamed it on ORM while other want to improve ActiveRercord performance by some precious tips which you can understand after at least over 5 years with Rails. Why we don't use another light weight ORM to replace ActiveRecord? The answer is Sequel, a rare project hasn't any issues yet on github (Good job, Sequel team!)
Different to ActiveRecord, a monolithic gem, Sequel is built based on modular architect when Core part contains just few needed functions and all others're kept in Extension/Plugins and used in necessary case. Because of this design, Sequel can run five time faster than Active record.
Besides, developer can increase flexibility by choosing suitable Sequel plugin or writing a separated module which can fit system's requirements. In this post instead of meantioning about the bright side of Sequel which has portraied in many blog posts(e.g http://twin.github.io/ode-to-sequel/) and comments on Github, I want to minimize the gap between Sequel and Rails developers who're almost familiar with ActiveRecord.
2.1 Basic setup
2.1.1 Clear away ActiveRecord
The first thing to do before bring Sequel to your life is elimiating everything of ActiveRecord. If you start a new project, let add -O or --skip-active-record params to your rails new command. If you want to get rid of ActiveRecord in existing project, let follow this:
- In config/application.rb, remove require rails/all and add these line:
require "action_controller/railtie" require "action_mailer/railtie" require "sprockets/railtie" require "rails/test_unit/railtie"
- In config/application.rb, remove
config.active_record.raise_in_transactional_callbacks = true
- In config/environments/development.rb, remove
config.active_record.migration_error = :page_load
- In config/environments/production.rb, remove
config.active_record.dump_schema_after_migration = false
It's notable that we don't have to remove config/database.yml and the next part will be shown the reason why we have to keep it.
2.1.2 Install Sequel
Basically, Sequel is made for pure Ruby code so it doesn't fully support Rails stack as ActiveRecord. This's meaning that we have to setup Database from the starting point, manual generating migration files and create various tasks to do for migrating new data.
To make a shortcut, TalentBox team has made a terrific gem named sequel-rails which aim to bring Sequel close to ActiveRecord users. For installation, just add this line to Gemfile then bundle
gem 'sequel-rails'
To connect to database, a plus point for sequel-rails when employing config/database.yml and similary to ActiveRecord we don't need to change anything in configuration. This is the sample configuration for mysql
development: adapter: mysql2 pool: 5 timeout: 5000 database: sample_sequel user: myuser password: 'mypassword'
Before start geeking, let spend few minutes to do some stuff in config files to make Sequel become your new home for development. These setting can be placed in config/application.rb or config/environments/ when you want some specify some extra functions for each environment
- For schema:
# Allowed options: :sql, :ruby. config.sequel.schema_format = :sql # Whether to dump the schema after successful migrations. # Defaults to false in production and test, true otherwise. config.sequel.schema_dump = true
- Overriding database.yml: Sure, we can do that but I prefer to do it in yml files
config.sequel.max_connections = 16
- Database tasks: Most exciting part, by default
config.sequel.load_database_tasks = true
When this flag's true, we can run database task as same as `ActiveRecord` commands but I'd like to specific command with `:sequel`
config.sequel.load_database_tasks = :sequel
then instead of `rake db:migration`, I'll let all the world know that I'm using `Sequel` when type `rake sequel:migration`
- Logger: indispensibale plugin that'll show off how fast of Sequel
config.sequel.logger = Logger.new($stdout)
- Default plugins: as mentioned above, the basic component of Sequel is plugin and definitely there're some plugins that you want to load by default. For example, we want to add timestamps into every model, so just push it into configuration
config.sequel.after_connect = proc do Sequel::Model.plugin :timestamps, update_on_create: true end
Ok, I think now we almost done and we can start to enjoy Sequel
2.2 Use Sequel like ActiveRecord
2.2.1 Migration
It's undeniable that ActiceRecord has provided a rich tasks working with rake command so sequel-rails gem has exploited this advantages of ActiveRecord. To print out all tasks that sequel-rails provides, let use rake -T or rake --tasks. And here're all tasks specific for migration
rake db:create[env] # Create the database defined in config/database.yml for the current Rails.env rake db:create:all # Create all the local databases defined in config/database.yml rake db:drop[env] # Drop the database defined in config/database.yml for the current Rails.env rake db:drop:all # Drops all the local databases defined in config/database.yml rake db:force_close_open_connections[env] # Forcibly close any open connections to the current env database (PostgreSQL specific) rake db:migrate # Migrate the database to the latest version rake db:migrate:down # Runs the "down" for a given migration VERSION rake db:migrate:redo # Rollbacks the database one migration and re migrate up rake db:migrate:reset # Resets your database using your migrations for the current environment rake db:migrate:up # Runs the "up" for a given migration VERSION rake db:reset # Drops and recreates the database from db/schema.rb for the current environment and loads the seeds rake db:rollback # Rollback the latest migration file or down to specified VERSION=x rake db:schema:dump # Create a db/schema.rb file that can be portably used against any DB supported by Sequel rake db:schema:load # Load a schema.rb file into the database rake db:seed # Load the seed data from db/seeds.rb rake db:setup # Create the database, load the schema, and initialize with the seed data rake db:structure:dump[env] # Dump the database structure to db/structure.sql rake db:test:prepare # Prepare test database (ensure all migrations ran, drop and re-create database then load schema)
As we can see, it's bacsically same as ActiveRecord tasks so we don't need to change the habit when working with migration. About migration files, when entering rails generate migration create_users, a new migration file's created follow timestamps format which's similar to ActiveRecord. Now let dig into content of migration files
Sequel.migration do change do create_table :users do primary_key :id String :name DateTime :created_at DateTime :updated_at end end end
There're some different points when working Sequel migration:
- primary_key and foregin_key: If id of record is implicit in ActiveRecord, Sequel requires to define by primary_key. However, the appearance of foregin_key help us to easily define the relation with other tables. E.g: when an user has many posts
Sequel.migration do change do create_table :posts do primary_key :id foreign_key :user_id, :users, index: true String :content, text: true DateTime :created_at DateTime :updated_at end end end
- index: Just adding the params index: true in the column
- Timestamps: for created_at and updated_at columns, we need to place it into record and enable plugin :timestamps inside Model
- Data types: let a glance at this migration file
Sequel.migraion do change do create_table(:columns_types) do Integer :a0 # integer String :a1 # varchar(255) String :a2, :size=>50 # varchar(50) String :a3, :fixed=>true # char(255) String :a4, :fixed=>true, :size=>50 # char(50) String :a5, :text=>true # text File :b # blob Fixnum :c # integer Bignum :d # bigint Float :e # double precision BigDecimal :f # numeric BigDecimal :f2, :size=>10 # numeric(10) BigDecimal :f3, :size=>[10, 2] # numeric(10, 2) Date :g # date DateTime :h # timestamp Time :i # timestamp Time :i2, :only_time=>true # time Numeric :j # numeric TrueClass :k # boolean FalseClass :l # boolean end end end
What I love in Sequel migration's simple, functional but still comprehensive. Same datatype is wrap into same group, if we want large space for them, just specify in optional fields. That's why we don't need `Text` type because it's almost same with `String` except size (lol). Besisdes, with `TrueClass` and `FalseClass`, we can forget about `Boolean, default: true/false`. So interesting!
2.2.2 Model
I think this part we can refer from document page of Sequel because everything I write will be duplicated from Github page
Sometimes changing's deeply paintful, especially with something that belongs to soul but it's not imposible. Let take my posts as a reference for using Sequel in Rails until it replaces completely ActiveRecord in next versions of Ruby on Rails.