lhm 1.0.0.rc.1

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.
@@ -0,0 +1,42 @@
1
+ #
2
+ # Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
3
+ # Schmidt
4
+ #
5
+
6
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
7
+
8
+ require 'lhm/table'
9
+ require 'lhm/migrator'
10
+
11
+ describe Lhm::Intersection do
12
+ include UnitHelper
13
+
14
+ it "should not have dropped changes" do
15
+ origin = Lhm::Table.new("origin")
16
+ origin.columns["dropped"] = varchar
17
+ origin.columns["retained"] = varchar
18
+
19
+ destination = Lhm::Table.new("destination")
20
+ destination.columns["retained"] = varchar
21
+
22
+ intersection = Lhm::Intersection.new(origin, destination)
23
+ intersection.common.include?("dropped").must_equal(false)
24
+ end
25
+
26
+ it "should have unchanged columns" do
27
+ origin = Lhm::Table.new("origin")
28
+ origin.columns["dropped"] = varchar
29
+ origin.columns["retained"] = varchar
30
+
31
+ destination = Lhm::Table.new("destination")
32
+ destination.columns["retained"] = varchar
33
+
34
+ intersection = Lhm::Intersection.new(origin, destination)
35
+ intersection.common.must_equal(["retained"])
36
+ end
37
+
38
+ def varchar
39
+ { :metadata => "VARCHAR(255)"}
40
+ end
41
+ end
42
+
@@ -0,0 +1,54 @@
1
+ #
2
+ # Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
3
+ # Schmidt
4
+ #
5
+
6
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
7
+
8
+ require 'lhm/table'
9
+ require 'lhm/migration'
10
+ require 'lhm/locked_switcher'
11
+
12
+ describe Lhm::LockedSwitcher do
13
+ include UnitHelper
14
+
15
+ before(:each) do
16
+ @start = Time.now
17
+ @origin = Lhm::Table.new("origin")
18
+ @destination = Lhm::Table.new("destination")
19
+ @migration = Lhm::Migration.new(@origin, @destination, @start)
20
+ @switcher = Lhm::LockedSwitcher.new(@migration)
21
+ end
22
+
23
+ describe "uncommitted" do
24
+ it "should disable autocommit first" do
25
+ @switcher.
26
+ statements[0..1].
27
+ must_equal([
28
+ "set @lhm_auto_commit = @@session.autocommit",
29
+ "set session autocommit = 0"
30
+ ])
31
+ end
32
+
33
+ it "should reapply original autocommit settings at the end" do
34
+ @switcher.
35
+ statements[-1].
36
+ must_equal("set session autocommit = @lhm_auto_commit")
37
+ end
38
+ end
39
+
40
+ describe "switch" do
41
+ it "should lock origin and destination table, switch, commit and unlock" do
42
+ @switcher.
43
+ switch.
44
+ must_equal([
45
+ "lock table `origin` write, `destination` write",
46
+ "alter table `origin` rename `#{ @migration.archive_name }`",
47
+ "alter table `destination` rename `origin`",
48
+ "commit",
49
+ "unlock tables"
50
+ ])
51
+ end
52
+ end
53
+ end
54
+
@@ -0,0 +1,26 @@
1
+ #
2
+ # Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
3
+ # Schmidt
4
+ #
5
+
6
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
7
+
8
+ require 'lhm/table'
9
+ require 'lhm/migration'
10
+
11
+ describe Lhm::Migration do
12
+ include UnitHelper
13
+
14
+ before(:each) do
15
+ @start = Time.now
16
+ @origin = Lhm::Table.new("origin")
17
+ @destination = Lhm::Table.new("destination")
18
+ @migration = Lhm::Migration.new(@origin, @destination, @start)
19
+ end
20
+
21
+ it "should name archive" do
22
+ stamp = "%Y_%m_%d_%H_%M_%S_#{ "%03d" % (@start.usec / 1000) }"
23
+ @migration.archive_name.must_equal "lhma_#{ @start.strftime(stamp) }_origin"
24
+ end
25
+ end
26
+
@@ -0,0 +1,81 @@
1
+ #
2
+ # Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
3
+ # Schmidt
4
+ #
5
+
6
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
7
+
8
+ require 'lhm/table'
9
+ require 'lhm/migrator'
10
+
11
+ describe Lhm::Migrator do
12
+ include UnitHelper
13
+
14
+ before(:each) do
15
+ @table = Lhm::Table.new("alt")
16
+ @creator = Lhm::Migrator.new(@table)
17
+ end
18
+
19
+ describe "index changes" do
20
+ it "should add an index" do
21
+ @creator.add_index(["a", "b"])
22
+
23
+ @creator.statements.must_equal([
24
+ "create index `index_alt_on_a_and_b` on lhmn_alt(a, b)"
25
+ ])
26
+ end
27
+
28
+ it "should remove an index" do
29
+ @creator.remove_index(["b", "a"])
30
+
31
+ @creator.statements.must_equal([
32
+ "drop index `index_alt_on_b_and_a` on `lhmn_alt`"
33
+ ])
34
+ end
35
+ end
36
+
37
+ describe "column changes" do
38
+ it "should add a column" do
39
+ @creator.add_column("logins", "INT(12)")
40
+
41
+ @creator.statements.must_equal([
42
+ "alter table `lhmn_alt` add column `logins` INT(12)"
43
+ ])
44
+ end
45
+
46
+ it "should remove a column" do
47
+ @creator.remove_column("logins")
48
+
49
+ @creator.statements.must_equal([
50
+ "alter table `lhmn_alt` drop `logins`"
51
+ ])
52
+ end
53
+ end
54
+
55
+ describe "direct changes" do
56
+ it "should accept a ddl statement" do
57
+ ddl = @creator.ddl("alter table `%s` add column `f` tinyint(1)" % @creator.name)
58
+
59
+ @creator.statements.must_equal([
60
+ "alter table `lhmn_alt` add column `f` tinyint(1)"
61
+ ])
62
+ end
63
+ end
64
+
65
+ describe "multiple changes" do
66
+ it "should add two columns" do
67
+ @creator.add_column("first", "VARCHAR(64)")
68
+ @creator.add_column("last", "VARCHAR(64)")
69
+ @creator.statements.length.must_equal(2)
70
+
71
+ @creator.
72
+ statements[0].
73
+ must_equal("alter table `lhmn_alt` add column `first` VARCHAR(64)")
74
+
75
+ @creator.
76
+ statements[1].
77
+ must_equal("alter table `lhmn_alt` add column `last` VARCHAR(64)")
78
+ end
79
+ end
80
+ end
81
+
@@ -0,0 +1,88 @@
1
+ #
2
+ # Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
3
+ # Schmidt
4
+ #
5
+
6
+ require File.expand_path(File.dirname(__FILE__)) + '/unit_helper'
7
+
8
+ require 'lhm/table'
9
+
10
+ describe Lhm::Table do
11
+ include UnitHelper
12
+
13
+ describe "names" do
14
+ before(:each) do
15
+ @table = Lhm::Table::Parser.new(fixture("users.ddl")).parse
16
+ end
17
+
18
+ it "should name destination" do
19
+ @table.destination_name.must_equal "lhmn_users"
20
+ end
21
+
22
+ it "should name index with a single column" do
23
+ @table.
24
+ idx_name(["name"]).
25
+ must_equal("index_users_on_name")
26
+ end
27
+
28
+ it "should name index with a multiple columns" do
29
+ @table.
30
+ idx_name(["name", "firstname"]).
31
+ must_equal("index_users_on_name_and_firstname")
32
+ end
33
+ end
34
+
35
+ describe "constraints" do
36
+ it "should be satisfied with a single column primary key called id" do
37
+ @table = Lhm::Table.new("table", "id")
38
+ @table.satisfies_primary_key?.must_equal true
39
+ end
40
+
41
+ it "should not be satisfied with a primary key unless called id" do
42
+ @table = Lhm::Table.new("table", "uuid")
43
+ @table.satisfies_primary_key?.must_equal false
44
+ end
45
+
46
+ it "should not be satisfied with multicolumn primary key" do
47
+ @table = Lhm::Table.new("table", ["id", "secondary"])
48
+ @table.satisfies_primary_key?.must_equal false
49
+ end
50
+ end
51
+
52
+ describe Lhm::Table::Parser do
53
+ describe "create table parsing" do
54
+ before(:each) do
55
+ @table = Lhm::Table::Parser.new(fixture("users.ddl")).parse
56
+ end
57
+
58
+ it "should parse table name in show create table" do
59
+ @table.name.must_equal("users")
60
+ end
61
+
62
+ it "should parse primary key" do
63
+ @table.pk.must_equal("id")
64
+ end
65
+
66
+ it "should parse column type in show create table" do
67
+ @table.columns["username"][:type].must_equal("varchar(255)")
68
+ end
69
+
70
+ it "should parse column metadata" do
71
+ @table.columns["username"][:metadata].must_equal("DEFAULT NULL")
72
+ end
73
+
74
+ it "should parse indices in show create table" do
75
+ @table.
76
+ indices["index_users_on_username_and_created_at"][:metadata].
77
+ must_equal("(`username`,`created_at`)")
78
+ end
79
+
80
+ it "should parse indices in show create table" do
81
+ @table.
82
+ indices["index_users_on_reference"][:metadata].
83
+ must_equal("(`reference`)")
84
+ end
85
+ end
86
+ end
87
+ end
88
+
@@ -0,0 +1,17 @@
1
+ #
2
+ # Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
3
+ # Schmidt
4
+ #
5
+
6
+ require File.expand_path(File.dirname(__FILE__)) + "/../bootstrap"
7
+
8
+ module UnitHelper
9
+ def fixture(name)
10
+ File.read $fixtures.join(name)
11
+ end
12
+
13
+ def strip(sql)
14
+ sql.strip.gsub(/\n */, "\n")
15
+ end
16
+ end
17
+
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lhm
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: true
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ - rc
10
+ - 1
11
+ version: 1.0.0.rc.1
12
+ platform: ruby
13
+ authors:
14
+ - SoundCloud
15
+ - Rany Keddo
16
+ - Tobias Bielohlawek
17
+ - Tobias Schmidt
18
+ autorequire:
19
+ bindir: bin
20
+ cert_chain: []
21
+
22
+ date: 2012-01-16 00:00:00 +01:00
23
+ default_executable:
24
+ dependencies:
25
+ - !ruby/object:Gem::Dependency
26
+ name: mysql
27
+ prerelease: false
28
+ requirement: &id001 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ segments:
34
+ - 2
35
+ - 8
36
+ - 1
37
+ version: 2.8.1
38
+ type: :development
39
+ version_requirements: *id001
40
+ - !ruby/object:Gem::Dependency
41
+ name: rspec
42
+ prerelease: false
43
+ requirement: &id002 !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - "="
47
+ - !ruby/object:Gem::Version
48
+ segments:
49
+ - 1
50
+ - 3
51
+ - 1
52
+ version: 1.3.1
53
+ type: :development
54
+ version_requirements: *id002
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ prerelease: false
58
+ requirement: &id003 !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ type: :development
67
+ version_requirements: *id003
68
+ 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
+ email: rany@soundcloud.com, tobi@soundcloud.com, ts@soundcloud.com
70
+ executables: []
71
+
72
+ extensions: []
73
+
74
+ extra_rdoc_files: []
75
+
76
+ files:
77
+ - .gitignore
78
+ - .travis.yml
79
+ - CHANGELOG.md
80
+ - Gemfile
81
+ - Gemfile.lock
82
+ - LICENSE
83
+ - README.md
84
+ - Rakefile
85
+ - TODO
86
+ - lhm.gemspec
87
+ - lib/lhm.rb
88
+ - lib/lhm/chunker.rb
89
+ - lib/lhm/command.rb
90
+ - lib/lhm/entangler.rb
91
+ - lib/lhm/intersection.rb
92
+ - lib/lhm/invoker.rb
93
+ - lib/lhm/locked_switcher.rb
94
+ - lib/lhm/migration.rb
95
+ - lib/lhm/migrator.rb
96
+ - lib/lhm/table.rb
97
+ - spec/bootstrap.rb
98
+ - spec/fixtures/destination.ddl
99
+ - spec/fixtures/origin.ddl
100
+ - spec/fixtures/users.ddl
101
+ - spec/integration/chunker_spec.rb
102
+ - spec/integration/entangler_spec.rb
103
+ - spec/integration/integration_helper.rb
104
+ - spec/integration/lhm_spec.rb
105
+ - spec/integration/locked_switcher_spec.rb
106
+ - spec/unit/chunker_spec.rb
107
+ - spec/unit/entangler_spec.rb
108
+ - spec/unit/intersection_spec.rb
109
+ - spec/unit/locked_switcher_spec.rb
110
+ - spec/unit/migration_spec.rb
111
+ - spec/unit/migrator_spec.rb
112
+ - spec/unit/table_spec.rb
113
+ - spec/unit/unit_helper.rb
114
+ has_rdoc: true
115
+ homepage: http://github.com/soundcloud/large-hadron-migrator
116
+ licenses: []
117
+
118
+ post_install_message:
119
+ rdoc_options: []
120
+
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ segments:
129
+ - 0
130
+ version: "0"
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ">"
135
+ - !ruby/object:Gem::Version
136
+ segments:
137
+ - 1
138
+ - 3
139
+ - 1
140
+ version: 1.3.1
141
+ requirements: []
142
+
143
+ rubyforge_project:
144
+ rubygems_version: 1.3.7
145
+ signing_key:
146
+ specification_version: 3
147
+ summary: online schema changer for mysql
148
+ test_files:
149
+ - spec/bootstrap.rb
150
+ - spec/fixtures/destination.ddl
151
+ - spec/fixtures/origin.ddl
152
+ - spec/fixtures/users.ddl
153
+ - spec/integration/chunker_spec.rb
154
+ - spec/integration/entangler_spec.rb
155
+ - spec/integration/integration_helper.rb
156
+ - spec/integration/lhm_spec.rb
157
+ - spec/integration/locked_switcher_spec.rb
158
+ - spec/unit/chunker_spec.rb
159
+ - spec/unit/entangler_spec.rb
160
+ - spec/unit/intersection_spec.rb
161
+ - spec/unit/locked_switcher_spec.rb
162
+ - spec/unit/migration_spec.rb
163
+ - spec/unit/migrator_spec.rb
164
+ - spec/unit/table_spec.rb
165
+ - spec/unit/unit_helper.rb