activerecord-safer_migrations 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 75845456397be277043e0b89d6fedbac416d3fe3
4
+ data.tar.gz: 01add6f99123fd1bd0246f41504ec754d674dc87
5
+ SHA512:
6
+ metadata.gz: b95179e85acb6a179fe9a05f018a4775fad27680bdb8f5945dbe43cc12187a53d91b13e4567097721df8ad5f14cdd465d68fb6aedc0896d86e19214e5b932d24
7
+ data.tar.gz: dbcd63bb99f1202a72d7a01b7857af9635bcce72d1c9703bedfa3035b607b9fcf2752bcb9953bba2b54412b0b46702f93a35c79355b7f78edf1d5729cd035148
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ /.bundle
data/.rubocop.yml ADDED
@@ -0,0 +1,30 @@
1
+ AllCops:
2
+ DisplayCopNames: true
3
+
4
+ # Use trailing rather than leading dots on multi-line call chains
5
+ Style/DotPosition:
6
+ EnforcedStyle: trailing
7
+
8
+ Style/Documentation:
9
+ Enabled: false
10
+
11
+ Style/StringLiterals:
12
+ EnforcedStyle: double_quotes
13
+
14
+ Style/TrailingComma:
15
+ EnforcedStyleForMultiline: comma
16
+
17
+ Style/AccessorMethodName:
18
+ Enabled: false
19
+
20
+ Style/SignalException:
21
+ EnforcedStyle: only_raise
22
+
23
+ Style/GlobalVars:
24
+ Enabled: false
25
+
26
+ Style/FileName:
27
+ Enabled: false
28
+
29
+ Metrics/LineLength:
30
+ Max: 100
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0-p353
data/.travis.yml ADDED
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.2
5
+
6
+ addons:
7
+ postgresql: "9.3"
8
+
9
+ before_script:
10
+ - psql -c 'CREATE DATABASE safer_migrations_test;' -U postgres
11
+
12
+ script:
13
+ - bundle exec rspec spec
14
+ - bundle exec rubocop
15
+
16
+ env:
17
+ - "DATABASE_URL=postgres://postgres@localhost/safer_migrations_test"
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem "pg", "~> 0.18.3"
7
+ gem "rspec", "~> 3.3.0"
8
+ gem "rubocop", "~> 0.35.1"
9
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,73 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ activerecord-safer_migrations (0.1.0)
5
+ activerecord (~> 4.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (4.2.5)
11
+ activesupport (= 4.2.5)
12
+ builder (~> 3.1)
13
+ activerecord (4.2.5)
14
+ activemodel (= 4.2.5)
15
+ activesupport (= 4.2.5)
16
+ arel (~> 6.0)
17
+ activesupport (4.2.5)
18
+ i18n (~> 0.7)
19
+ json (~> 1.7, >= 1.7.7)
20
+ minitest (~> 5.1)
21
+ thread_safe (~> 0.3, >= 0.3.4)
22
+ tzinfo (~> 1.1)
23
+ arel (6.0.3)
24
+ ast (2.1.0)
25
+ astrolabe (1.3.1)
26
+ parser (~> 2.2)
27
+ builder (3.2.2)
28
+ diff-lcs (1.2.5)
29
+ i18n (0.7.0)
30
+ json (1.8.3)
31
+ minitest (5.8.3)
32
+ parser (2.2.3.0)
33
+ ast (>= 1.1, < 3.0)
34
+ pg (0.18.3)
35
+ powerpack (0.1.1)
36
+ rainbow (2.0.0)
37
+ rspec (3.3.0)
38
+ rspec-core (~> 3.3.0)
39
+ rspec-expectations (~> 3.3.0)
40
+ rspec-mocks (~> 3.3.0)
41
+ rspec-core (3.3.2)
42
+ rspec-support (~> 3.3.0)
43
+ rspec-expectations (3.3.1)
44
+ diff-lcs (>= 1.2.0, < 2.0)
45
+ rspec-support (~> 3.3.0)
46
+ rspec-mocks (3.3.2)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.3.0)
49
+ rspec-support (3.3.0)
50
+ rubocop (0.35.1)
51
+ astrolabe (~> 1.3)
52
+ parser (>= 2.2.3.0, < 3.0)
53
+ powerpack (~> 0.1)
54
+ rainbow (>= 1.99.1, < 3.0)
55
+ ruby-progressbar (~> 1.7)
56
+ tins (<= 1.6.0)
57
+ ruby-progressbar (1.7.5)
58
+ thread_safe (0.3.5)
59
+ tins (1.6.0)
60
+ tzinfo (1.2.2)
61
+ thread_safe (~> 0.1)
62
+
63
+ PLATFORMS
64
+ ruby
65
+
66
+ DEPENDENCIES
67
+ activerecord-safer_migrations!
68
+ pg (~> 0.18.3)
69
+ rspec (~> 3.3.0)
70
+ rubocop (~> 0.35.1)
71
+
72
+ BUNDLED WITH
73
+ 1.10.6
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 GoCardless Ltd.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ ## ActiveRecord safer migration helpers
2
+
3
+ *Note: this library only supports PostgreSQL 9.3+. If you're interested in adding support for other databases, we're open to pull requests!*
4
+
5
+ Postgres holds ACCESS EXCLUSIVE locks for [almost all][pg-alter-table] DDL
6
+ operations. ACCESS EXCLUSIVE locks conflict with all other table-level locks,
7
+ which can cause issues in several situations. For instance:
8
+
9
+ 1. If the lock is held for a long time, all other access to the table will be
10
+ blocked, which can result in downtime.
11
+ 2. Even if the lock is only held breifly, it will block all other access to the
12
+ table while it is in the lock queue, as it conflicts with all other locks.
13
+ The lock can't be acquired until all other queries ahead of it have finished,
14
+ so having to wait on long-running queries can also result in downtime.
15
+ See [here][blog-post] for more details.
16
+
17
+ Both these issues can be avoided by setting timeouts on the migration connection -
18
+ `statement_timeout` and `lock_timeout` respectively.
19
+
20
+ Once this gem is loaded, all migrations will automatically have a
21
+ `lock_timeout` and a `statement_timeout` set. The initial `lock_timeout`
22
+ default is 750ms, and the initial `statement_timeout` default is 1500ms. Both
23
+ defaults can be easily changed (e.g. in a Rails initializer).
24
+
25
+ ```ruby
26
+ ActiveRecord::SaferMigrations.default_lock_timeout = 1000
27
+ ActiveRecord::SaferMigrations.default_statement_timeout = 2000
28
+ ```
29
+
30
+ To explicitly set timeouts for a given migration, use the `set_lock_timeout` and
31
+ `set_statement_timeout` class methods in the migration.
32
+
33
+ ```ruby
34
+ class LockTest < ActiveRecord::Migration
35
+ set_lock_timeout(250)
36
+ set_statement_timeout(750)
37
+
38
+ def change
39
+ create_table :lock_test
40
+ end
41
+ end
42
+ ```
43
+
44
+ To disable timeouts for a migration, use the `disable_lock_timeout!` and
45
+ `disable_statement_timeout!` class methods. Note that this is [extremely
46
+ dangerous][blog-post] if you're doing any schema alterations in your migration.
47
+
48
+ ```ruby
49
+ class LockTest < ActiveRecord::Migration
50
+ # Only do this if you really know what you're doing!
51
+ disable_lock_timeout!
52
+ disable_statement_timeout!
53
+
54
+ def change
55
+ create_table :lock_test
56
+ end
57
+ end
58
+ ```
59
+
60
+ ### Use with PgBouncer
61
+
62
+ This gem sets session-level settings on Postgres connections. If you're using
63
+ PgBouncer in transaction pooling mode, using session-level settings is
64
+ dangerous, as you can't guarantee which connection will receive the setting.
65
+ For this reason, this gem is incompatible with transaction-pooling and should
66
+ only be used if migrations are run on connections that support session-level
67
+ features.
68
+
69
+ [blog-post]: https://gocardless.com/blog/zero-downtime-postgres-migrations-the-hard-parts/
70
+ [pg-alter-table]: http://www.postgresql.org/docs/9.4/static/sql-altertable.html
71
+
@@ -0,0 +1,17 @@
1
+ require File.expand_path("../lib/active_record/safer_migrations/version", __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = "activerecord-safer_migrations"
5
+ gem.version = ActiveRecord::SaferMigrations::VERSION
6
+ gem.date = "2015-08-10"
7
+ gem.summary = "ActiveRecord migration helpers to avoid downtime"
8
+ gem.description = ""
9
+ gem.authors = ["GoCardless Engineering"]
10
+ gem.email = "developers@gocardless.com"
11
+ gem.files = `git ls-files`.split("\n")
12
+ gem.require_paths = ["lib"]
13
+ gem.homepage = "https://github.com/gocardless/activerecord-safer_migrations"
14
+ gem.license = "MIT"
15
+
16
+ gem.add_runtime_dependency "activerecord", "~> 4.2"
17
+ end
@@ -0,0 +1,64 @@
1
+ require "active_record/safer_migrations/setting_helper"
2
+
3
+ module ActiveRecord
4
+ module SaferMigrations
5
+ module Migration
6
+ def self.included(base)
7
+ base.class_eval do
8
+ # Use Rails' class_attribute to get an attribute that you can
9
+ # override in subclasses
10
+ class_attribute :lock_timeout
11
+ class_attribute :statement_timeout
12
+
13
+ prepend(InstanceMethods)
14
+ extend(ClassMethods)
15
+ end
16
+ end
17
+
18
+ module InstanceMethods
19
+ def exec_migration(conn, direction)
20
+ # lock_timeout is an instance accessor created by class_attribute
21
+ lock_timeout_ms = lock_timeout || SaferMigrations.default_lock_timeout
22
+ statement_timeout_ms = statement_timeout || SaferMigrations.default_statement_timeout
23
+ SettingHelper.new(conn, :lock_timeout, lock_timeout_ms).with_setting do
24
+ SettingHelper.new(conn, :statement_timeout, statement_timeout_ms).with_setting do
25
+ super(conn, direction)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ module ClassMethods
32
+ def set_lock_timeout(timeout)
33
+ if timeout == 0
34
+ raise "Setting lock_timeout to 0 is dangerous - it disables the lock " \
35
+ "timeout rather than instantly timing out. If you *actually* " \
36
+ "want to disable the lock timeout (not recommended!), use the " \
37
+ "`disable_lock_timeout!` method."
38
+ end
39
+ self.lock_timeout = timeout
40
+ end
41
+
42
+ def disable_lock_timeout!
43
+ say "WARNING: disabling the lock timeout. This is very dangerous."
44
+ self.lock_timeout = 0
45
+ end
46
+
47
+ def set_statement_timeout(timeout)
48
+ if timeout == 0
49
+ raise "Setting statement_timeout to 0 is dangerous - it disables the statement " \
50
+ "timeout rather than instantly timing out. If you *actually* " \
51
+ "want to disable the statement timeout (not recommended!), use the " \
52
+ "`disable_statement_timeout!` method."
53
+ end
54
+ self.statement_timeout = timeout
55
+ end
56
+
57
+ def disable_statement_timeout!
58
+ say "WARNING: disabling the statement timeout. This is very dangerous."
59
+ self.statement_timeout = 0
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,38 @@
1
+ module ActiveRecord
2
+ module SaferMigrations
3
+ module PostgreSQLAdapter
4
+ SET_SETTING_SQL = <<-SQL
5
+ UPDATE
6
+ pg_settings
7
+ SET
8
+ setting = :value
9
+ WHERE
10
+ name = :setting_name
11
+ SQL
12
+
13
+ GET_SETTING_SQL = <<-SQL
14
+ SELECT
15
+ setting
16
+ FROM
17
+ pg_settings
18
+ WHERE
19
+ name = :setting_name
20
+ SQL
21
+
22
+ def set_setting(setting_name, value)
23
+ sql = fill_sql_values(SET_SETTING_SQL, value: value, setting_name: setting_name)
24
+ execute(sql)
25
+ end
26
+
27
+ def get_setting(setting_name)
28
+ sql = fill_sql_values(GET_SETTING_SQL, setting_name: setting_name)
29
+ result = execute(sql)
30
+ result.first["setting"]
31
+ end
32
+
33
+ def fill_sql_values(sql, values)
34
+ ActiveRecord::Base.send(:replace_named_bind_variables, sql, values)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,11 @@
1
+ module ActiveRecord
2
+ module SaferMigrations
3
+ class Railtie < Rails::Railtie
4
+ initializer "active_record_safer_migrations.load_adapter" do
5
+ ActiveSupport.on_load :active_record do
6
+ ActiveRecord::SaferMigrations.load
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,49 @@
1
+ module ActiveRecord
2
+ module SaferMigrations
3
+ class SettingHelper
4
+ def initialize(connection, setting_name, value)
5
+ @connection = connection
6
+ @setting_name = setting_name
7
+ @value = value
8
+ end
9
+
10
+ # We're changing a connection level setting, and we need to make sure we return
11
+ # it to the original value. It is automatically reverted if set within a
12
+ # transaction which rolls back, so that case needs handling differently.
13
+ #
14
+ # | In Transaction | Not in transaction
15
+ # ---------------------------------------------------------
16
+ # Raises | Reset setting | Reset setting
17
+ # Doesn't raise | Don't reset setting | Reset setting
18
+ def with_setting
19
+ record_current_setting
20
+ set_new_setting
21
+ yield
22
+ reset_setting
23
+ rescue
24
+ reset_setting unless in_transaction?
25
+ raise
26
+ end
27
+
28
+ private
29
+
30
+ def record_current_setting
31
+ @original_value = @connection.get_setting(@setting_name)
32
+ end
33
+
34
+ def set_new_setting
35
+ puts "-- set_setting(#{@setting_name.inspect}, #{@value})"
36
+ @connection.set_setting(@setting_name, @value)
37
+ end
38
+
39
+ def reset_setting
40
+ puts "-- set_setting(#{@setting_name.inspect}, #{@original_value})"
41
+ @connection.set_setting(@setting_name, @original_value)
42
+ end
43
+
44
+ def in_transaction?
45
+ ActiveRecord::Base.connection.open_transactions > 0
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,5 @@
1
+ module ActiveRecord
2
+ module SaferMigrations
3
+ VERSION = "0.1.0".freeze
4
+ end
5
+ end
@@ -0,0 +1,38 @@
1
+ require "active_record/connection_adapters/postgresql_adapter"
2
+ require "active_record/safer_migrations/postgresql_adapter"
3
+ require "active_record/safer_migrations/migration"
4
+
5
+ module ActiveRecord
6
+ module SaferMigrations
7
+ @default_lock_timeout = 750
8
+ @default_statement_timeout = 1500
9
+
10
+ def self.default_lock_timeout
11
+ @default_lock_timeout
12
+ end
13
+
14
+ def self.default_lock_timeout=(timeout_ms)
15
+ @default_lock_timeout = timeout_ms
16
+ end
17
+
18
+ def self.default_statement_timeout
19
+ @default_statement_timeout
20
+ end
21
+
22
+ def self.default_statement_timeout=(timeout_ms)
23
+ @default_statement_timeout = timeout_ms
24
+ end
25
+
26
+ def self.load
27
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
28
+ include ActiveRecord::SaferMigrations::PostgreSQLAdapter
29
+ end
30
+
31
+ ActiveRecord::Migration.class_eval do
32
+ include ActiveRecord::SaferMigrations::Migration
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ require "active_record/safer_migrations/railtie" if defined?(::Rails)
@@ -0,0 +1,160 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe ActiveRecord::SaferMigrations::Migration do
4
+ before { nuke_migrations }
5
+ before { TimeoutTestHelpers.set(:lock_timeout, 0) }
6
+ before { TimeoutTestHelpers.set(:statement_timeout, 0) }
7
+
8
+ describe "setting timeouts explicitly" do
9
+ before { $lock_timeout = nil }
10
+ before { $statement_timeout = nil }
11
+
12
+ shared_examples_for "running the migration" do
13
+ let(:migration) do
14
+ Class.new(ActiveRecord::Migration) do
15
+ set_lock_timeout(5000)
16
+ set_statement_timeout(5001)
17
+
18
+ def change
19
+ $lock_timeout = TimeoutTestHelpers.get(:lock_timeout)
20
+ $statement_timeout = TimeoutTestHelpers.get(:statement_timeout)
21
+ end
22
+ end
23
+ end
24
+
25
+ it "sets the lock timeout for the duration of the migration" do
26
+ silence_stream($stdout) { run_migration.call }
27
+ expect($lock_timeout).to eq(5000)
28
+ end
29
+
30
+ it "unsets the lock timeout after the migration" do
31
+ silence_stream($stdout) { run_migration.call }
32
+ expect(TimeoutTestHelpers.get(:lock_timeout)).to eq(0)
33
+ end
34
+
35
+ it "sets the statement timeout for the duration of the migration" do
36
+ silence_stream($stdout) { run_migration.call }
37
+ expect($statement_timeout).to eq(5001)
38
+ end
39
+
40
+ it "unsets the statement timeout after the migration" do
41
+ silence_stream($stdout) { run_migration.call }
42
+ expect(TimeoutTestHelpers.get(:statement_timeout)).to eq(0)
43
+ end
44
+
45
+ context "when the original timeout is not 0" do
46
+ before { TimeoutTestHelpers.set(:lock_timeout, 8000) }
47
+ before { TimeoutTestHelpers.set(:statement_timeout, 8001) }
48
+
49
+ it "unsets the lock timeout after the migration" do
50
+ silence_stream($stdout) { run_migration.call }
51
+ expect(TimeoutTestHelpers.get(:lock_timeout)).to eq(8000)
52
+ end
53
+
54
+ it "unsets the statement timeout after the migration" do
55
+ silence_stream($stdout) { run_migration.call }
56
+ expect(TimeoutTestHelpers.get(:statement_timeout)).to eq(8001)
57
+ end
58
+ end
59
+ end
60
+
61
+ context "when running with transactional DDL" do
62
+ let(:run_migration) do
63
+ -> { ActiveRecord::Base.transaction { migration.migrate(:up) } }
64
+ end
65
+
66
+ include_examples "running the migration"
67
+ end
68
+
69
+ context "when running without transactional DDL" do
70
+ let(:run_migration) { -> { migration.migrate(:up) } }
71
+
72
+ include_examples "running the migration"
73
+ end
74
+ end
75
+
76
+ describe "the default timeouts" do
77
+ before { $lock_timeout = nil }
78
+ before { $statement_timeout = nil }
79
+ before { ActiveRecord::SaferMigrations.default_lock_timeout = 6000 }
80
+ before { ActiveRecord::SaferMigrations.default_statement_timeout = 6001 }
81
+ let(:migration) do
82
+ Class.new(ActiveRecord::Migration) do
83
+ def change
84
+ $lock_timeout = TimeoutTestHelpers.get(:lock_timeout)
85
+ $statement_timeout = TimeoutTestHelpers.get(:statement_timeout)
86
+ end
87
+ end
88
+ end
89
+
90
+ it "sets the lock timeout for the duration of the migration" do
91
+ silence_stream($stdout) { migration.migrate(:up) }
92
+ expect($lock_timeout).to eq(6000)
93
+ end
94
+
95
+ it "unsets the lock timeout after the migration" do
96
+ silence_stream($stdout) { migration.migrate(:up) }
97
+ expect(TimeoutTestHelpers.get(:lock_timeout)).to eq(0)
98
+ end
99
+
100
+ it "sets the statement timeout for the duration of the migration" do
101
+ silence_stream($stdout) { migration.migrate(:up) }
102
+ expect($statement_timeout).to eq(6001)
103
+ end
104
+
105
+ it "unsets the statement timeout after the migration" do
106
+ silence_stream($stdout) { migration.migrate(:up) }
107
+ expect(TimeoutTestHelpers.get(:statement_timeout)).to eq(0)
108
+ end
109
+ end
110
+
111
+ describe "when inheriting from a migration with timeouts defined" do
112
+ before { $lock_timeout = nil }
113
+ before { $statement_timeout = nil }
114
+ before { ActiveRecord::SaferMigrations.default_lock_timeout = 6000 }
115
+ before { ActiveRecord::SaferMigrations.default_statement_timeout = 6001 }
116
+ let(:base_migration) do
117
+ Class.new(ActiveRecord::Migration) do
118
+ set_lock_timeout(7000)
119
+ set_statement_timeout(7001)
120
+ def change
121
+ $lock_timeout = TimeoutTestHelpers.get(:lock_timeout)
122
+ $statement_timeout = TimeoutTestHelpers.get(:statement_timeout)
123
+ end
124
+ end
125
+ end
126
+
127
+ context "when the timeout isn't overridden" do
128
+ let(:migration) { Class.new(base_migration) {} }
129
+
130
+ it "sets the base class' lock timeout for the duration of the migration" do
131
+ silence_stream($stdout) { migration.migrate(:up) }
132
+ expect($lock_timeout).to eq(7000)
133
+ end
134
+
135
+ it "sets the base class' statement timeout for the duration of the migration" do
136
+ silence_stream($stdout) { migration.migrate(:up) }
137
+ expect($statement_timeout).to eq(7001)
138
+ end
139
+ end
140
+
141
+ context "when the timeout is overridden" do
142
+ let(:migration) do
143
+ Class.new(base_migration) do
144
+ set_lock_timeout(8000)
145
+ set_statement_timeout(8001)
146
+ end
147
+ end
148
+
149
+ it "sets the subclass' lock timeout for the duration of the migration" do
150
+ silence_stream($stdout) { migration.migrate(:up) }
151
+ expect($lock_timeout).to eq(8000)
152
+ end
153
+
154
+ it "sets the subclass' statement timeout for the duration of the migration" do
155
+ silence_stream($stdout) { migration.migrate(:up) }
156
+ expect($statement_timeout).to eq(8001)
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,39 @@
1
+ require "active_record"
2
+ require "activerecord-safer_migrations"
3
+
4
+ ActiveRecord::SaferMigrations.load
5
+ ActiveRecord::Base.establish_connection
6
+
7
+ def silence_stream(stream)
8
+ old_stream = stream.dup
9
+ stream.reopen(IO::NULL)
10
+ stream.sync = true
11
+ yield
12
+ ensure
13
+ stream.reopen(old_stream)
14
+ old_stream.close
15
+ end
16
+
17
+ def nuke_migrations
18
+ ActiveRecord::Base.connection_pool.with_connection do |conn|
19
+ conn.execute("DROP TABLE IF EXISTS schema_migrations")
20
+ end
21
+ end
22
+
23
+ module TimeoutTestHelpers
24
+ def self.get(timeout_name)
25
+ sql = <<-SQL
26
+ SELECT
27
+ setting AS #{timeout_name}
28
+ FROM
29
+ pg_settings
30
+ WHERE
31
+ name = '#{timeout_name}'
32
+ SQL
33
+ ActiveRecord::Base.connection.execute(sql).first[timeout_name.to_s].to_i
34
+ end
35
+
36
+ def self.set(timeout_name, timeout)
37
+ ActiveRecord::Base.connection.execute("SET #{timeout_name} = #{timeout}")
38
+ end
39
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-safer_migrations
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - GoCardless Engineering
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '4.2'
27
+ description: ''
28
+ email: developers@gocardless.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - .gitignore
34
+ - .rubocop.yml
35
+ - .ruby-version
36
+ - .travis.yml
37
+ - Gemfile
38
+ - Gemfile.lock
39
+ - LICENSE.txt
40
+ - README.md
41
+ - activerecord-safer_migrations.gemspec
42
+ - lib/active_record/safer_migrations/migration.rb
43
+ - lib/active_record/safer_migrations/postgresql_adapter.rb
44
+ - lib/active_record/safer_migrations/railtie.rb
45
+ - lib/active_record/safer_migrations/setting_helper.rb
46
+ - lib/active_record/safer_migrations/version.rb
47
+ - lib/activerecord-safer_migrations.rb
48
+ - spec/active_record/safer_migrations/migration_spec.rb
49
+ - spec/spec_helper.rb
50
+ homepage: https://github.com/gocardless/activerecord-safer_migrations
51
+ licenses:
52
+ - MIT
53
+ metadata: {}
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 2.0.14
71
+ signing_key:
72
+ specification_version: 4
73
+ summary: ActiveRecord migration helpers to avoid downtime
74
+ test_files: []