Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Migrating to Single Table Inheritance

Migrating to Single Table Inheritance

How Shopify migrated from a regular ActiveRecord model to a Single Table Inheritance (STI) with zero downtime

Steps include:
- Adding a type column
- Renaming the table
- Creating the STI

Guillaume Malette

July 16, 2013
Tweet

More Decks by Guillaume Malette

Other Decks in Programming

Transcript

  1. class CustomerSearch < Search class ProductSearch < Search class Search

    < AR::Base class OrderSearch < Search Single Table Inheritance Allows having `Search < AR::Base` and models inheriting from Search
  2. Class Single Table Inheritance When Rails loads the models, it

    instantiates the class in the `type` column
  3. Step 1: Add the `type` column Step 1: Add the

    `type` column Adding columns in MySQL takes time and blocks the table.
  4.        Add a step

    to deployment, LHM Think of LHM as asynchronous, non-blocking migrations
  5. def self.up Lhm.change_table :customer_groups do |m| m.add_column :type, "VARCHAR(32) DEFAULT

    'CustomerSearch'" end end Large Hadron Migrator After this we’ll have a `type` column What will be the problem if we push this as-is?
  6. Step 2: Creating a view At some point in time,

    both the old and new code will be running Both `customer_groups` and `searches` tables will be expected Table rename is out of the scope of responsibilities for LHM
  7. class CustomerSearch < Search class ProductSearch < Search class Search

    < AR::Base class OrderSearch < Search Write these classes
  8. Notice the default value is 0, and extra doesn’t show

    autoincrement The id is also not described as being the primary key
  9. after_initialize :reset_id def reset_id self.id = nil if self.id ==

    0 end Whenever a model is initialized with ID 0, reset it to nil Rails will insert as-is, MySQL will auto increment and everything will be fine
  10. N def up execute("RENAME TABLE searches TO searches_view_to_drop, customer_groups TO

    searches") execute("DROP VIEW searches_view_to_drop") end