lhm 1.0.0.rc.1 → 1.0.0.rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +1 -1
- data/CHANGELOG.md +13 -3
- data/README.md +2 -2
- data/Rakefile +3 -0
- data/lhm.gemspec +3 -3
- data/lib/lhm.rb +1 -1
- data/lib/lhm/chunker.rb +8 -4
- data/lib/lhm/command.rb +13 -10
- data/lib/lhm/intersection.rb +1 -1
- data/lib/lhm/locked_switcher.rb +3 -2
- data/lib/lhm/migrator.rb +20 -3
- data/lib/lhm/table.rb +2 -1
- data/spec/integration/integration_helper.rb +15 -4
- data/spec/integration/lhm_spec.rb +9 -1
- data/spec/unit/migrator_spec.rb +17 -1
- data/spec/unit/table_spec.rb +13 -1
- metadata +17 -29
- data/TODO +0 -11
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,18 @@
|
|
1
|
-
# 1.0.0
|
1
|
+
# 1.0.0.rc2 (January 18, 2012)
|
2
2
|
|
3
|
-
*
|
3
|
+
* Speedup migrations for tables with large ids
|
4
|
+
* Fix conversion of milliseconds to seconds
|
5
|
+
* Fix handling of sql errors
|
6
|
+
* Add helper to create unique index
|
7
|
+
* Allow index creation on prefix of column
|
8
|
+
* Quote column names on index creation
|
9
|
+
* Remove ambiguous method signature
|
10
|
+
* Documentation fix
|
11
|
+
* 1.8.7 compatibility
|
12
|
+
|
13
|
+
# 1.0.0.rc1 (January 15, 2012)
|
4
14
|
|
5
|
-
|
15
|
+
* rewrite.
|
6
16
|
|
7
17
|
# 0.2.1 (November 26, 2011)
|
8
18
|
|
data/README.md
CHANGED
@@ -42,11 +42,11 @@ twitter solution [1], it does not require the presence of an indexed
|
|
42
42
|
|
43
43
|
## Usage
|
44
44
|
|
45
|
-
After
|
45
|
+
After extending Lhm, `hadron_change_table` becomes available
|
46
46
|
with the following methods:
|
47
47
|
|
48
48
|
class MigrateArbitrary < ActiveRecord::Migration
|
49
|
-
|
49
|
+
extend Lhm
|
50
50
|
|
51
51
|
def self.up
|
52
52
|
hadron_change_table(:users) do |t|
|
data/Rakefile
CHANGED
data/lhm.gemspec
CHANGED
@@ -3,8 +3,6 @@
|
|
3
3
|
lib = File.expand_path('../lib', __FILE__)
|
4
4
|
$:.unshift(lib) unless $:.include?(lib)
|
5
5
|
|
6
|
-
puts $:.inspect
|
7
|
-
|
8
6
|
require 'lhm'
|
9
7
|
|
10
8
|
Gem::Specification.new do |s|
|
@@ -23,7 +21,9 @@ Gem::Specification.new do |s|
|
|
23
21
|
|
24
22
|
# this should be a real dependency, but we're using a different gem in our code
|
25
23
|
s.add_development_dependency "mysql", "~> 2.8.1"
|
26
|
-
s.add_development_dependency "
|
24
|
+
s.add_development_dependency "minitest", "= 2.10.0"
|
27
25
|
s.add_development_dependency "rake"
|
26
|
+
|
27
|
+
s.add_dependency "activerecord"
|
28
28
|
end
|
29
29
|
|
data/lib/lhm.rb
CHANGED
data/lib/lhm/chunker.rb
CHANGED
@@ -29,7 +29,7 @@ module Lhm
|
|
29
29
|
|
30
30
|
def up_to(limit)
|
31
31
|
traversable_chunks_up_to(limit).times do |n|
|
32
|
-
yield(bottom(n + 1), top(n + 1, limit))
|
32
|
+
yield(bottom(n + 1), top(n + 1, limit))
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -59,14 +59,18 @@ module Lhm
|
|
59
59
|
|
60
60
|
def execute
|
61
61
|
up_to(@limit) do |lowest, highest|
|
62
|
-
|
62
|
+
affected_rows = update(copy(lowest, highest))
|
63
|
+
|
64
|
+
if affected_rows > 0
|
65
|
+
sleep(throttle_seconds)
|
66
|
+
end
|
63
67
|
|
64
|
-
|
68
|
+
print "."
|
65
69
|
end
|
66
70
|
end
|
67
71
|
|
68
72
|
def throttle_seconds
|
69
|
-
@throttle /
|
73
|
+
@throttle / 1000.0
|
70
74
|
end
|
71
75
|
end
|
72
76
|
end
|
data/lib/lhm/command.rb
CHANGED
@@ -17,9 +17,7 @@ module Lhm
|
|
17
17
|
|
18
18
|
def validate; end
|
19
19
|
|
20
|
-
def revert
|
21
|
-
raise NotImplementedError.new(self.class.name)
|
22
|
-
end
|
20
|
+
def revert; end
|
23
21
|
|
24
22
|
def run(&block)
|
25
23
|
validate
|
@@ -56,14 +54,19 @@ module Lhm
|
|
56
54
|
end
|
57
55
|
|
58
56
|
def sql(statements)
|
59
|
-
[
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
57
|
+
[statements].flatten.each { |statement| @connection.execute(statement) }
|
58
|
+
rescue ActiveRecord::StatementInvalid, Mysql::Error => e
|
59
|
+
revert
|
60
|
+
error e.message
|
61
|
+
end
|
62
|
+
|
63
|
+
def update(statements)
|
64
|
+
[statements].flatten.inject(0) do |memo, statement|
|
65
|
+
memo += @connection.update(statement)
|
66
66
|
end
|
67
|
+
rescue ActiveRecord::StatementInvalid, Mysql::Error => e
|
68
|
+
revert
|
69
|
+
error e.message
|
67
70
|
end
|
68
71
|
end
|
69
72
|
end
|
data/lib/lhm/intersection.rb
CHANGED
data/lib/lhm/locked_switcher.rb
CHANGED
data/lib/lhm/migrator.rb
CHANGED
@@ -61,7 +61,20 @@ module Lhm
|
|
61
61
|
#
|
62
62
|
|
63
63
|
def add_index(cols)
|
64
|
-
ddl = "create index `%s` on %s" %
|
64
|
+
ddl = "create index `%s` on %s" % idx_parts(cols)
|
65
|
+
statements << ddl.strip
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Add a unique index to a table:
|
70
|
+
#
|
71
|
+
# hadron_change_table("users") do |t|
|
72
|
+
# t.add_unique_index([:comment, :created_at])
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
|
76
|
+
def add_unique_index(cols)
|
77
|
+
ddl = "create unique index `%s` on %s" % idx_parts(cols)
|
65
78
|
statements << ddl.strip
|
66
79
|
end
|
67
80
|
|
@@ -73,7 +86,7 @@ module Lhm
|
|
73
86
|
# end
|
74
87
|
#
|
75
88
|
|
76
|
-
def remove_index(
|
89
|
+
def remove_index(cols)
|
77
90
|
ddl = "drop index `%s` on `%s`" % [@origin.idx_name(cols), @name]
|
78
91
|
statements << ddl.strip
|
79
92
|
end
|
@@ -118,7 +131,11 @@ module Lhm
|
|
118
131
|
end
|
119
132
|
|
120
133
|
def idx_spec(cols)
|
121
|
-
"
|
134
|
+
"`#{ @name }` (#{ Array(cols).map(&:to_s).join(', ') })"
|
135
|
+
end
|
136
|
+
|
137
|
+
def idx_parts(cols)
|
138
|
+
[@origin.idx_name(cols), idx_spec(cols)]
|
122
139
|
end
|
123
140
|
end
|
124
141
|
end
|
data/lib/lhm/table.rb
CHANGED
@@ -24,7 +24,8 @@ module Lhm
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def idx_name(cols)
|
27
|
-
|
27
|
+
column_part = Array(cols).map { |c| c.to_s.sub(/\(.*/, "") }.join("_and_")
|
28
|
+
"index_#{ @name }_on_#{ column_part }"
|
28
29
|
end
|
29
30
|
|
30
31
|
def self.parse(table_name, connection)
|
@@ -9,7 +9,6 @@ require 'active_record'
|
|
9
9
|
require 'lhm/table'
|
10
10
|
|
11
11
|
module IntegrationHelper
|
12
|
-
delegate :select_one, :select_value, :execute, :to => :connection
|
13
12
|
|
14
13
|
#
|
15
14
|
# Connectivity
|
@@ -30,6 +29,18 @@ module IntegrationHelper
|
|
30
29
|
ActiveRecord::Base.connection
|
31
30
|
end
|
32
31
|
|
32
|
+
def select_one(*args)
|
33
|
+
connection.select_one(*args)
|
34
|
+
end
|
35
|
+
|
36
|
+
def select_value(*args)
|
37
|
+
connection.select_value(*args)
|
38
|
+
end
|
39
|
+
|
40
|
+
def execute(*args)
|
41
|
+
connection.execute(*args)
|
42
|
+
end
|
43
|
+
|
33
44
|
#
|
34
45
|
# Test Data
|
35
46
|
#
|
@@ -66,9 +77,9 @@ module IntegrationHelper
|
|
66
77
|
select_value(query).to_i
|
67
78
|
end
|
68
79
|
|
69
|
-
def key?(table, cols)
|
70
|
-
|
80
|
+
def key?(table, cols, type = :non_unique)
|
81
|
+
non_unique = type == :non_unique ? 1 : 0
|
82
|
+
query = "show indexes in #{ table.name } where key_name = '#{ table.idx_name(cols) }' and non_unique = #{ non_unique }"
|
71
83
|
!!select_value(query)
|
72
84
|
end
|
73
85
|
end
|
74
|
-
|
@@ -55,9 +55,17 @@ describe Lhm do
|
|
55
55
|
key?(table_read("users"), ["comment", "created_at"]).must_equal(true)
|
56
56
|
end
|
57
57
|
|
58
|
+
it "should add a unqiue index" do
|
59
|
+
hadron_change_table("users") do |t|
|
60
|
+
t.add_unique_index(:comment)
|
61
|
+
end
|
62
|
+
|
63
|
+
key?(table_read(:users), :comment, :unique).must_equal(true)
|
64
|
+
end
|
65
|
+
|
58
66
|
it "should remove an index" do
|
59
67
|
hadron_change_table("users") do |t|
|
60
|
-
t.remove_index(:username, :created_at)
|
68
|
+
t.remove_index([:username, :created_at])
|
61
69
|
end
|
62
70
|
|
63
71
|
key?(table_read("users"), ["username", "created_at"]).must_equal(false)
|
data/spec/unit/migrator_spec.rb
CHANGED
@@ -21,7 +21,23 @@ describe Lhm::Migrator do
|
|
21
21
|
@creator.add_index(["a", "b"])
|
22
22
|
|
23
23
|
@creator.statements.must_equal([
|
24
|
-
"create index `index_alt_on_a_and_b` on lhmn_alt(a, b)"
|
24
|
+
"create index `index_alt_on_a_and_b` on `lhmn_alt` (a, b)"
|
25
|
+
])
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should add an index with prefixed columns" do
|
29
|
+
@creator.add_index(["a(10)", "b"])
|
30
|
+
|
31
|
+
@creator.statements.must_equal([
|
32
|
+
"create index `index_alt_on_a_and_b` on `lhmn_alt` (a(10), b)"
|
33
|
+
])
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should add an unique index" do
|
37
|
+
@creator.add_unique_index(["a(10)", :b])
|
38
|
+
|
39
|
+
@creator.statements.must_equal([
|
40
|
+
"create unique index `index_alt_on_a_and_b` on `lhmn_alt` (a(10), b)"
|
25
41
|
])
|
26
42
|
end
|
27
43
|
|
data/spec/unit/table_spec.rb
CHANGED
@@ -25,11 +25,23 @@ describe Lhm::Table do
|
|
25
25
|
must_equal("index_users_on_name")
|
26
26
|
end
|
27
27
|
|
28
|
-
it "should name index with
|
28
|
+
it "should name index with multiple columns" do
|
29
29
|
@table.
|
30
30
|
idx_name(["name", "firstname"]).
|
31
31
|
must_equal("index_users_on_name_and_firstname")
|
32
32
|
end
|
33
|
+
|
34
|
+
it "should name index with prefixed column" do
|
35
|
+
@table.
|
36
|
+
idx_name(["name(10)", "firstname"]).
|
37
|
+
must_equal("index_users_on_name_and_firstname")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should name index with column names given as symbol" do
|
41
|
+
@table.
|
42
|
+
idx_name([:name, :firstname]).
|
43
|
+
must_equal("index_users_on_name_and_firstname")
|
44
|
+
end
|
33
45
|
end
|
34
46
|
|
35
47
|
describe "constraints" do
|
metadata
CHANGED
@@ -1,14 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lhm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 1
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- rc
|
10
|
-
- 1
|
11
|
-
version: 1.0.0.rc.1
|
4
|
+
prerelease: 6
|
5
|
+
version: 1.0.0.rc2
|
12
6
|
platform: ruby
|
13
7
|
authors:
|
14
8
|
- SoundCloud
|
@@ -19,7 +13,7 @@ autorequire:
|
|
19
13
|
bindir: bin
|
20
14
|
cert_chain: []
|
21
15
|
|
22
|
-
date: 2012-01-
|
16
|
+
date: 2012-01-18 00:00:00 +01:00
|
23
17
|
default_executable:
|
24
18
|
dependencies:
|
25
19
|
- !ruby/object:Gem::Dependency
|
@@ -30,26 +24,18 @@ dependencies:
|
|
30
24
|
requirements:
|
31
25
|
- - ~>
|
32
26
|
- !ruby/object:Gem::Version
|
33
|
-
segments:
|
34
|
-
- 2
|
35
|
-
- 8
|
36
|
-
- 1
|
37
27
|
version: 2.8.1
|
38
28
|
type: :development
|
39
29
|
version_requirements: *id001
|
40
30
|
- !ruby/object:Gem::Dependency
|
41
|
-
name:
|
31
|
+
name: minitest
|
42
32
|
prerelease: false
|
43
33
|
requirement: &id002 !ruby/object:Gem::Requirement
|
44
34
|
none: false
|
45
35
|
requirements:
|
46
36
|
- - "="
|
47
37
|
- !ruby/object:Gem::Version
|
48
|
-
|
49
|
-
- 1
|
50
|
-
- 3
|
51
|
-
- 1
|
52
|
-
version: 1.3.1
|
38
|
+
version: 2.10.0
|
53
39
|
type: :development
|
54
40
|
version_requirements: *id002
|
55
41
|
- !ruby/object:Gem::Dependency
|
@@ -60,11 +46,20 @@ dependencies:
|
|
60
46
|
requirements:
|
61
47
|
- - ">="
|
62
48
|
- !ruby/object:Gem::Version
|
63
|
-
segments:
|
64
|
-
- 0
|
65
49
|
version: "0"
|
66
50
|
type: :development
|
67
51
|
version_requirements: *id003
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: activerecord
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id004
|
68
63
|
description: Migrate large tables without downtime by copying to a temporary table in chunks. The old table is not dropped. Instead, it is moved to timestamp_table_name for verification.
|
69
64
|
email: rany@soundcloud.com, tobi@soundcloud.com, ts@soundcloud.com
|
70
65
|
executables: []
|
@@ -82,7 +77,6 @@ files:
|
|
82
77
|
- LICENSE
|
83
78
|
- README.md
|
84
79
|
- Rakefile
|
85
|
-
- TODO
|
86
80
|
- lhm.gemspec
|
87
81
|
- lib/lhm.rb
|
88
82
|
- lib/lhm/chunker.rb
|
@@ -125,23 +119,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
125
119
|
requirements:
|
126
120
|
- - ">="
|
127
121
|
- !ruby/object:Gem::Version
|
128
|
-
segments:
|
129
|
-
- 0
|
130
122
|
version: "0"
|
131
123
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
124
|
none: false
|
133
125
|
requirements:
|
134
126
|
- - ">"
|
135
127
|
- !ruby/object:Gem::Version
|
136
|
-
segments:
|
137
|
-
- 1
|
138
|
-
- 3
|
139
|
-
- 1
|
140
128
|
version: 1.3.1
|
141
129
|
requirements: []
|
142
130
|
|
143
131
|
rubyforge_project:
|
144
|
-
rubygems_version: 1.
|
132
|
+
rubygems_version: 1.5.0
|
145
133
|
signing_key:
|
146
134
|
specification_version: 3
|
147
135
|
summary: online schema changer for mysql
|