3. class CompletelySafeMigration < ActiveRecord::Migration
def self.up
remove_column :users, :notes
end
def self.down
add_column :users, :notes, :text
end
end
4. class CompletelySafeMigration < ActiveRecord::Migration
def self.up
remove_column :users, :notes
end
def self.down
add_column :users, :notes, :text
end
end
19. Hot compatibility
v1 v1’ v2
writes to `notes` stop writing to `notes` drop column `notes`
20. Hot compatibility
v1 v1’ v2
writes to `notes` stop writing to `notes` drop column `notes`
21. Hot compatibility
v1 v1’ v2
writes to `notes` stop writing to `notes` drop column `notes`
22. Hot compatibility
v1 v1’ v2
writes to `notes` stop writing to `notes` drop column `notes`
23. Hot compatibility
v1 v1’ v2
writes to `notes` stop writing to `notes` drop column `notes`
class User
def self.columns
super.reject { |c| c.name == "notes" }
end
end
30. Self dependency
• Make controllers compatible:
class AuthController < ApplicationController
protected
def filtered_params
params.dup.tap do |p|
p.merge!(:email => p.delete(:username))
end
end
end
31. Assets - Rails 2
Server 1
GET /dashboard
200
GET /stylesheets/all.js
200
32. Assets - Rails 2
Server 1 Server 2
GET /dashboard
200
GET /stylesheets/all.js
404
37. Asset pipeline - Rails 3
• When updating assets, keep the existing
version around
38. Asset pipeline - Rails 3
• When updating assets, keep the existing
version around
• We’re still in need of tooling to help
cleaning up old assets
39. Asset pipeline - Rails 3
• When updating assets, keep the existing
version around
• We’re still in need of tooling to help
cleaning up old assets
rake assets:clean:outdated
50. InnoDB
• O(n) locks on:
• Adding, updating or removing columns
• Adding or removing indexes
51. InnoDB
1. Create a new table with the same structure
2. Apply changes to new table
3. Copy data over
4. Acquire lock on original table
5. Sync new/updated/deleted rows
6. Swap tables
7. Release lock
52. InnoDB
1. Create a new table with the same structure
2. Apply changes to new table
3. Copy data over
4. Acquire lock on original table
5. Sync new/updated/deleted rows
6. Swap tables
7. Release lock
56. Postgres
• Table locks on:
• Adding, updating or removing columns
• Adding or removing indexes
57. Postgres
• O(n) locks on:
• Adding, updating or removing columns
• Adding or removing indexes
58. Postgres
• O(n) locks on:
constant time if you avoid default
values and constraints
• Adding, updating or removing columns
• Adding or removing indexes
59. Postgres
• O(n) locks on: constant time
• Adding, updating or removing columns
• Adding or removing indexes
60. Postgres
• O(n) locks on:
• Adding, updating or removing columns
• Adding or removing indexes
can be done without any locking
61. Postgres
• O(n) locks on:
• Adding, updating or removing columns
• Adding or removing indexes
constant time
69. What about renaming?
v1 v1’ v1’’ v2
reads and writes add column `bar` populate `bar` reads and writes
to `foo` + where needed to `bar`
reads from `foo`
writes to both
`foo` and `bar`
70. What about renaming?
v2 v2’ v3
reads and writes ignore `foo` drop column `foo`
to `bar`
76. Workflow
• Make sure pull requests are addressing
hot compatibility
• Work after code is merged
77. Meanwhile, in production...
$ thin -C config/server.yml restart
Stopping server on 0.0.0.0:5000 ...
Sending QUIT signal to process 3463 ...
Stopping server on 0.0.0.0:5001 ...
Sending QUIT signal to process 3464 ...
Stopping server on 0.0.0.0:5002 ...
Sending QUIT signal to process 3465 ...
Starting server on 0.0.0.0:5000 ...
Starting server on 0.0.0.0:5001 ...
Starting server on 0.0.0.0:5002 ...
78. Meanwhile, in production...
$ thin -C config/server.yml restart
Stopping server on 0.0.0.0:5000 ...
Sending QUIT signal to process 3463 ...
Stopping server on 0.0.0.0:5001 ...
Sending QUIT signal to process 3464 ...
Stopping server on 0.0.0.0:5002 ...
Sending QUIT signal to process 3465 ...
Starting server on 0.0.0.0:5000 ...
Starting server on 0.0.0.0:5001 ...
Starting server on 0.0.0.0:5002 ...
79. Meanwhile, in production...
$ thin -C config/server.yml restart
Stopping server on 0.0.0.0:5000 ...
Sending QUIT signal to process 3463 ...
Stopping server on 0.0.0.0:5001 ...
Sending QUIT signal to process 3464 ...
Stopping server on 0.0.0.0:5002 ...
Sending QUIT signal to process 3465 ...
Starting server on 0.0.0.0:5000 ...
Starting server on 0.0.0.0:5001 ...
Starting server on 0.0.0.0:5002 ...
80. Meanwhile, in production...
$ thin -C config/server.yml restart
Stopping server on 0.0.0.0:5000 ...
Sending QUIT signal to process 3463 ...
Stopping server on 0.0.0.0:5001 ...
Sending QUIT signal to process 3464 ...
Stopping server on 0.0.0.0:5002 ...
Sending QUIT signal to process 3465 ...
Starting server on 0.0.0.0:5000 ...
Starting server on 0.0.0.0:5001 ...
Starting server on 0.0.0.0:5002 ...
81. Meanwhile, in production...
$ thin -C config/server.yml restart
Stopping server on 0.0.0.0:5000 ...
Sending QUIT signal to process 3463 ...
Stopping server on 0.0.0.0:5001 ...
Sending QUIT signal to process 3464 ...
Stopping server on 0.0.0.0:5002 ...
Sending QUIT signal to process 3465 ...
Starting server on 0.0.0.0:5000 ...
Starting server on 0.0.0.0:5001 ...
Starting server on 0.0.0.0:5002 ...
89. Above the stack
• Use your favorite web server
• Setup a load balancer above them, route
around servers that are cycling
90. Above the stack
• PROTIP: default setup won’t work
• Configure load balancer to poll for your
application health:
class HealthController < ApplicationController
def index
if File.exists?(Rails.root.join("MAINTENANCE"))
render :text => "Forget me!", :status => 503
else
render :text => "I'm alive!"
end
end
end
91. Cycling + Load balancer
Server Load balancer
touch MAINTENANCE
bounce servers remove server from rotation
rm MAINTENANCE
new servers come online
add server to rotation
92. HAProxy
option httpchk GET /health
listen myapp :9000
server myapp-1 1.0.0.1:5000 maxconn 1 check inter 2000 fall 1 rise 1
server myapp-2 1.0.0.2:5000 maxconn 1 check inter 2000 fall 1 rise 1
93. HAProxy
tell HAProxy where to check for the health
option httpchk GET /health
listen myapp :9000
server myapp-1 1.0.0.1:5000 maxconn 1 check inter 2000 fall 1 rise 1
server myapp-2 1.0.0.2:5000 maxconn 1 check inter 2000 fall 1 rise 1
94. HAProxy
option httpchk GET /health
listen myapp :9000
server myapp-1 1.0.0.1:5000 maxconn 1 check inter 2000 fall 1 rise 1
server myapp-2 1.0.0.2:5000 maxconn 1 check inter 2000 fall 1 rise 1
do not let your web server queue requests
95. HAProxy
option httpchk GET /health
listen myapp :9000
server myapp-1 1.0.0.1:5000 maxconn 1 check inter 2000 fall 1 rise 1
server myapp-2 1.0.0.2:5000 maxconn 1 check inter 2000 fall 1 rise 1
tell HAProxy to run the health check specified above
96. HAProxy
option httpchk GET /health
listen myapp :9000
server myapp-1 1.0.0.1:5000 maxconn 1 check inter 2000 fall 1 rise 1
server myapp-2 1.0.0.2:5000 maxconn 1 check inter 2000 fall 1 rise 1
every 2s
97. HAProxy
option httpchk GET /health
listen myapp :9000
server myapp-1 1.0.0.1:5000 maxconn 1 check inter 2000 fall 1 rise 1
server myapp-2 1.0.0.2:5000 maxconn 1 check inter 2000 fall 1 rise 1
stop routing to it once the health check failed once
98. HAProxy
option httpchk GET /health
listen myapp :9000
server myapp-1 1.0.0.1:5000 maxconn 1 check inter 2000 fall 1 rise 1
server myapp-2 1.0.0.2:5000 maxconn 1 check inter 2000 fall 1 rise 1
and bring it back once it worked once
117. Within the stack
• Easy to setup and deploy
• Optimal performance to
cycle the server
118. Within the stack
• Easy to setup and deploy • Changes your application
stack
• Optimal performance to
cycle the server
119. Within the stack
• Easy to setup and deploy • Changes your application
stack
• Optimal performance to
cycle the server • Doesn’t address availability
issues
121. Heroku
Process State Command
------- --------------- -------------------------------
web.1 up for 10h bundle exec thin start -p $PORT
122. Heroku
$ git push heroku master
Process State Command
------- --------------- -------------------------------
web.1 up for 10h bundle exec thin start -p $PORT
123. Heroku
$ git push heroku master
Process State Command
------- --------------- -------------------------------
web.1 starting for 1s bundle exec thin start -p $PORT
124. Heroku
Process State Command
------- --------------- -------------------------------
web.1 starting for 5s bundle exec thin start -p $PORT
125. Heroku
Process State Command
------- --------------- -------------------------------
web.1 up for 1s bundle exec thin start -p $PORT
126. Heroku BETA
Process State Command
------- --------------- -------------------------------
web.1 up for 10h bundle exec thin start -p $PORT
127. Heroku BETA
$ heroku labs:enable preboot
Process State Command
------- --------------- -------------------------------
web.1 up for 10h bundle exec thin start -p $PORT
128. Heroku BETA
Process State Command
------- --------------- -------------------------------
web.1 up for 10h bundle exec thin start -p $PORT
129. Heroku BETA
$ git push heroku master
Process State Command
------- --------------- -------------------------------
web.1 up for 10h bundle exec thin start -p $PORT
130. Heroku BETA
$ git push heroku master
Process State Command
------- --------------- -------------------------------
web.1 up for 10h bundle exec thin start -p $PORT
web.1 starting for 5s bundle exec thin start -p $PORT
131. Heroku BETA
Process State Command
------- --------------- -------------------------------
web.1 up for 10h bundle exec thin start -p $PORT
web.1 up for 1s bundle exec thin start -p $PORT
132. Heroku BETA
Process State Command
------- --------------- -------------------------------
web.1 up for 5s bundle exec thin start -p $PORT