sbader-lhm 1.1.0
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/.gitignore +6 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +99 -0
- data/LICENSE +27 -0
- data/README.md +146 -0
- data/Rakefile +20 -0
- data/bin/lhm-kill-queue +172 -0
- data/bin/lhm-spec-clobber.sh +36 -0
- data/bin/lhm-spec-grants.sh +25 -0
- data/bin/lhm-spec-setup-cluster.sh +67 -0
- data/bin/lhm-test-all.sh +10 -0
- data/gemfiles/ar-2.3_mysql.gemfile +5 -0
- data/gemfiles/ar-3.2_mysql.gemfile +5 -0
- data/gemfiles/ar-3.2_mysql2.gemfile +5 -0
- data/lhm.gemspec +27 -0
- data/lib/lhm.rb +45 -0
- data/lib/lhm/atomic_switcher.rb +49 -0
- data/lib/lhm/chunker.rb +114 -0
- data/lib/lhm/command.rb +46 -0
- data/lib/lhm/entangler.rb +98 -0
- data/lib/lhm/intersection.rb +63 -0
- data/lib/lhm/invoker.rb +49 -0
- data/lib/lhm/locked_switcher.rb +71 -0
- data/lib/lhm/migration.rb +30 -0
- data/lib/lhm/migrator.rb +219 -0
- data/lib/lhm/sql_helper.rb +85 -0
- data/lib/lhm/table.rb +97 -0
- data/lib/lhm/version.rb +6 -0
- data/spec/.lhm.example +4 -0
- data/spec/README.md +51 -0
- data/spec/bootstrap.rb +13 -0
- data/spec/fixtures/destination.ddl +6 -0
- data/spec/fixtures/origin.ddl +6 -0
- data/spec/fixtures/small_table.ddl +4 -0
- data/spec/fixtures/users.ddl +12 -0
- data/spec/integration/atomic_switcher_spec.rb +42 -0
- data/spec/integration/chunker_spec.rb +32 -0
- data/spec/integration/entangler_spec.rb +66 -0
- data/spec/integration/integration_helper.rb +140 -0
- data/spec/integration/lhm_spec.rb +204 -0
- data/spec/integration/locked_switcher_spec.rb +42 -0
- data/spec/integration/table_spec.rb +48 -0
- data/spec/unit/atomic_switcher_spec.rb +31 -0
- data/spec/unit/chunker_spec.rb +111 -0
- data/spec/unit/entangler_spec.rb +76 -0
- data/spec/unit/intersection_spec.rb +39 -0
- data/spec/unit/locked_switcher_spec.rb +51 -0
- data/spec/unit/migration_spec.rb +23 -0
- data/spec/unit/migrator_spec.rb +134 -0
- data/spec/unit/sql_helper_spec.rb +32 -0
- data/spec/unit/table_spec.rb +34 -0
- data/spec/unit/unit_helper.rb +14 -0
- metadata +173 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
# Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
module Lhm
|
5
|
+
module SqlHelper
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def annotation
|
9
|
+
"/* large hadron migration */"
|
10
|
+
end
|
11
|
+
|
12
|
+
def idx_name(table_name, cols)
|
13
|
+
column_names = column_definition(cols).map(&:first)
|
14
|
+
"index_#{ table_name }_on_#{ column_names.join("_and_") }"
|
15
|
+
end
|
16
|
+
|
17
|
+
def idx_spec(cols)
|
18
|
+
column_definition(cols).map do |name, length|
|
19
|
+
"`#{ name }`#{ length }"
|
20
|
+
end.join(', ')
|
21
|
+
end
|
22
|
+
|
23
|
+
def table?(table_name)
|
24
|
+
connection.table_exists?(table_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def sql(statements)
|
28
|
+
[statements].flatten.each do |statement|
|
29
|
+
connection.execute(tagged(statement))
|
30
|
+
end
|
31
|
+
rescue ActiveRecord::StatementInvalid => e
|
32
|
+
error e.message
|
33
|
+
end
|
34
|
+
|
35
|
+
def update(statements)
|
36
|
+
[statements].flatten.inject(0) do |memo, statement|
|
37
|
+
memo += connection.update(tagged(statement))
|
38
|
+
end
|
39
|
+
rescue ActiveRecord::StatementInvalid => e
|
40
|
+
error e.message
|
41
|
+
end
|
42
|
+
|
43
|
+
def version_string
|
44
|
+
connection.select_one("show variables like 'version'")["Value"]
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def tagged(statement)
|
50
|
+
"#{ statement } #{ SqlHelper.annotation }"
|
51
|
+
end
|
52
|
+
|
53
|
+
def column_definition(cols)
|
54
|
+
Array(cols).map do |column|
|
55
|
+
column.to_s.match(/`?([^\(]+)`?(\([^\)]+\))?/).captures
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Older versions of MySQL contain an atomic rename bug affecting bin
|
60
|
+
# log order. Affected versions extracted from bug report:
|
61
|
+
#
|
62
|
+
# http://bugs.mysql.com/bug.php?id=39675
|
63
|
+
#
|
64
|
+
# More Info: http://dev.mysql.com/doc/refman/5.5/en/metadata-locking.html
|
65
|
+
def supports_atomic_switch?
|
66
|
+
major, minor, tiny = version_string.split('.').map(&:to_i)
|
67
|
+
|
68
|
+
case major
|
69
|
+
when 4 then return false if minor and minor < 2
|
70
|
+
when 5
|
71
|
+
case minor
|
72
|
+
when 0 then return false if tiny and tiny < 52
|
73
|
+
when 1 then return false
|
74
|
+
when 4 then return false if tiny and tiny < 4
|
75
|
+
when 5 then return false if tiny and tiny < 3
|
76
|
+
end
|
77
|
+
when 6
|
78
|
+
case minor
|
79
|
+
when 0 then return false if tiny and tiny < 11
|
80
|
+
end
|
81
|
+
end
|
82
|
+
return true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/lhm/table.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require 'lhm/sql_helper'
|
5
|
+
|
6
|
+
module Lhm
|
7
|
+
class Table
|
8
|
+
attr_reader :name, :columns, :indices, :pk, :ddl
|
9
|
+
|
10
|
+
def initialize(name, pk = "id", ddl = nil)
|
11
|
+
@name = name
|
12
|
+
@columns = {}
|
13
|
+
@indices = {}
|
14
|
+
@pk = pk
|
15
|
+
@ddl = ddl
|
16
|
+
end
|
17
|
+
|
18
|
+
def satisfies_primary_key?
|
19
|
+
@pk == "id"
|
20
|
+
end
|
21
|
+
|
22
|
+
def destination_name
|
23
|
+
"lhmn_#{ @name }"
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.parse(table_name, connection)
|
27
|
+
Parser.new(table_name, connection).parse
|
28
|
+
end
|
29
|
+
|
30
|
+
class Parser
|
31
|
+
include SqlHelper
|
32
|
+
|
33
|
+
def initialize(table_name, connection)
|
34
|
+
@table_name = table_name.to_s
|
35
|
+
@schema_name = connection.current_database
|
36
|
+
@connection = connection
|
37
|
+
end
|
38
|
+
|
39
|
+
def ddl
|
40
|
+
sql = "show create table `#{ @table_name }`"
|
41
|
+
specification = nil
|
42
|
+
@connection.execute(sql).each { |row| specification = row.last }
|
43
|
+
specification
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse
|
47
|
+
schema = read_information_schema
|
48
|
+
|
49
|
+
Table.new(@table_name, extract_primary_key(schema), ddl).tap do |table|
|
50
|
+
schema.each do |defn|
|
51
|
+
table.columns[defn["COLUMN_NAME"]] = {
|
52
|
+
:type => defn["COLUMN_TYPE"],
|
53
|
+
:is_nullable => defn["IS_NULLABLE"],
|
54
|
+
:column_default => defn["COLUMN_DEFAULT"]
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
extract_indices(read_indices).each do |idx, columns|
|
59
|
+
table.indices[idx] = columns
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def read_information_schema
|
67
|
+
@connection.select_all %Q{
|
68
|
+
select *
|
69
|
+
from information_schema.columns
|
70
|
+
where table_name = "#{ @table_name }"
|
71
|
+
and table_schema = "#{ @schema_name }"
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def read_indices
|
76
|
+
@connection.select_all %Q{
|
77
|
+
show indexes from `#{ @schema_name }`.`#{ @table_name }`
|
78
|
+
where key_name != "PRIMARY"
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def extract_indices(indices)
|
83
|
+
indices.map { |row| [row["Key_name"], row["Column_name"]] }.
|
84
|
+
inject(Hash.new { |h, k| h[k] = []}) do |memo, (idx, column)|
|
85
|
+
memo[idx] << column
|
86
|
+
memo
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def extract_primary_key(schema)
|
91
|
+
cols = schema.select { |defn| defn["COLUMN_KEY"] == "PRI" }
|
92
|
+
keys = cols.map { |defn| defn["COLUMN_NAME"] }
|
93
|
+
keys.length == 1 ? keys.first : keys
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/lhm/version.rb
ADDED
data/spec/.lhm.example
ADDED
data/spec/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
Preparing for master slave integration tests
|
2
|
+
--------------------------------------------
|
3
|
+
|
4
|
+
# configuration
|
5
|
+
|
6
|
+
create ~/.lhm:
|
7
|
+
|
8
|
+
mysqldir=/usr/local/mysql
|
9
|
+
basedir=/opt/lhm-cluster
|
10
|
+
master_port=3306
|
11
|
+
slave_port=3307
|
12
|
+
|
13
|
+
mysqldir specifies the location of your mysql install. basedir is the
|
14
|
+
directory master and slave databases will get installed into.
|
15
|
+
|
16
|
+
# setup
|
17
|
+
|
18
|
+
You can set the integration specs up to run against a master slave setup by
|
19
|
+
running the included `bin/lhm-spec-clobber.sh` script. this deletes the configured
|
20
|
+
lhm master slave setup and reinstalls and configures a master slave setup.
|
21
|
+
|
22
|
+
Follow the manual instructions if you want more control over this process.
|
23
|
+
|
24
|
+
# manual setup
|
25
|
+
|
26
|
+
## set up instances
|
27
|
+
|
28
|
+
bin/lhm-spec-setup-cluster.sh
|
29
|
+
|
30
|
+
## start instances
|
31
|
+
|
32
|
+
basedir=/opt/lhm-luster
|
33
|
+
mysqld --defaults-file="$basedir/master/my.cnf"
|
34
|
+
mysqld --defaults-file="$basedir/slave/my.cnf"
|
35
|
+
|
36
|
+
## run the grants
|
37
|
+
|
38
|
+
bin/lhm-spec-grants.sh
|
39
|
+
|
40
|
+
## run specs
|
41
|
+
|
42
|
+
To run specs in slave mode, set the SLAVE=1 when running tests:
|
43
|
+
|
44
|
+
MASTER_SLAVE=1 rake specs
|
45
|
+
|
46
|
+
# connecting
|
47
|
+
|
48
|
+
you can connect by running (with the respective ports):
|
49
|
+
|
50
|
+
mysql --protocol=TCP -p3307
|
51
|
+
|
data/spec/bootstrap.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require 'minitest/spec'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
require 'minitest/mock'
|
7
|
+
require "pathname"
|
8
|
+
|
9
|
+
$project = Pathname.new(File.dirname(__FILE__) + '/..').cleanpath
|
10
|
+
$spec = $project.join("spec")
|
11
|
+
$fixtures = $spec.join("fixtures")
|
12
|
+
|
13
|
+
$: << $project.join("lib").to_s
|
@@ -0,0 +1,12 @@
|
|
1
|
+
CREATE TABLE `users` (
|
2
|
+
`id` int(11) NOT NULL AUTO_INCREMENT,
|
3
|
+
`reference` int(11) DEFAULT NULL,
|
4
|
+
`username` varchar(255) DEFAULT NULL,
|
5
|
+
`group` varchar(255) DEFAULT NULL,
|
6
|
+
`created_at` datetime DEFAULT NULL,
|
7
|
+
`comment` varchar(20) DEFAULT NULL,
|
8
|
+
`description` text,
|
9
|
+
PRIMARY KEY (`id`),
|
10
|
+
UNIQUE KEY `index_users_on_reference` (`reference`),
|
11
|
+
KEY `index_users_on_username_and_created_at` (`username`,`created_at`)
|
12
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__)) + '/integration_helper'
|
5
|
+
|
6
|
+
require 'lhm/table'
|
7
|
+
require 'lhm/migration'
|
8
|
+
require 'lhm/atomic_switcher'
|
9
|
+
|
10
|
+
describe Lhm::AtomicSwitcher do
|
11
|
+
include IntegrationHelper
|
12
|
+
|
13
|
+
before(:each) { connect_master! }
|
14
|
+
|
15
|
+
describe "switching" do
|
16
|
+
before(:each) do
|
17
|
+
@origin = table_create("origin")
|
18
|
+
@destination = table_create("destination")
|
19
|
+
@migration = Lhm::Migration.new(@origin, @destination)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "rename origin to archive" do
|
23
|
+
switcher = Lhm::AtomicSwitcher.new(@migration, connection)
|
24
|
+
switcher.run
|
25
|
+
|
26
|
+
slave do
|
27
|
+
table_exists?(@origin).must_equal true
|
28
|
+
table_read(@migration.archive_name).columns.keys.must_include "origin"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "rename destination to origin" do
|
33
|
+
switcher = Lhm::AtomicSwitcher.new(@migration, connection)
|
34
|
+
switcher.run
|
35
|
+
|
36
|
+
slave do
|
37
|
+
table_exists?(@destination).must_equal false
|
38
|
+
table_read(@origin.name).columns.keys.must_include "destination"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__)) + '/integration_helper'
|
5
|
+
|
6
|
+
require 'lhm'
|
7
|
+
require 'lhm/table'
|
8
|
+
require 'lhm/migration'
|
9
|
+
|
10
|
+
describe Lhm::Chunker do
|
11
|
+
include IntegrationHelper
|
12
|
+
|
13
|
+
before(:each) { connect_master! }
|
14
|
+
|
15
|
+
describe "copying" do
|
16
|
+
before(:each) do
|
17
|
+
@origin = table_create(:origin)
|
18
|
+
@destination = table_create(:destination)
|
19
|
+
@migration = Lhm::Migration.new(@origin, @destination)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should copy 23 rows from origin to destination" do
|
23
|
+
23.times { |n| execute("insert into origin set id = '#{ n * n + 23 }'") }
|
24
|
+
|
25
|
+
Lhm::Chunker.new(@migration, connection, { :stride => 100 }).run
|
26
|
+
|
27
|
+
slave do
|
28
|
+
count_all(@destination.name).must_equal(23)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__)) + '/integration_helper'
|
5
|
+
|
6
|
+
require 'lhm/table'
|
7
|
+
require 'lhm/migration'
|
8
|
+
require 'lhm/entangler'
|
9
|
+
|
10
|
+
describe Lhm::Entangler do
|
11
|
+
include IntegrationHelper
|
12
|
+
|
13
|
+
before(:each) { connect_master! }
|
14
|
+
|
15
|
+
describe "entanglement" do
|
16
|
+
before(:each) do
|
17
|
+
@origin = table_create("origin")
|
18
|
+
@destination = table_create("destination")
|
19
|
+
@migration = Lhm::Migration.new(@origin, @destination)
|
20
|
+
@entangler = Lhm::Entangler.new(@migration, connection)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should replay inserts from origin into destination" do
|
24
|
+
@entangler.run do |entangler|
|
25
|
+
execute("insert into origin (common) values ('inserted')")
|
26
|
+
end
|
27
|
+
|
28
|
+
slave do
|
29
|
+
count(:destination, "common", "inserted").must_equal(1)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should replay deletes from origin into destination" do
|
34
|
+
execute("insert into origin (common) values ('inserted')")
|
35
|
+
|
36
|
+
@entangler.run do |entangler|
|
37
|
+
execute("delete from origin where common = 'inserted'")
|
38
|
+
end
|
39
|
+
|
40
|
+
slave do
|
41
|
+
count(:destination, "common", "inserted").must_equal(0)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should replay updates from origin into destination" do
|
46
|
+
@entangler.run do |entangler|
|
47
|
+
execute("insert into origin (common) values ('inserted')")
|
48
|
+
execute("update origin set common = 'updated'")
|
49
|
+
end
|
50
|
+
|
51
|
+
slave do
|
52
|
+
count(:destination, "common", "updated").must_equal(1)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should remove entanglement" do
|
57
|
+
@entangler.run {}
|
58
|
+
|
59
|
+
execute("insert into origin (common) values ('inserted')")
|
60
|
+
|
61
|
+
slave do
|
62
|
+
count(:destination, "common", "inserted").must_equal(0)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# Copyright (c) 2011, SoundCloud Ltd., Rany Keddo, Tobias Bielohlawek, Tobias
|
2
|
+
# Schmidt
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__)) + "/../bootstrap"
|
5
|
+
|
6
|
+
require 'active_record'
|
7
|
+
begin
|
8
|
+
require 'mysql2'
|
9
|
+
rescue LoadError
|
10
|
+
require 'mysql'
|
11
|
+
end
|
12
|
+
require 'lhm/table'
|
13
|
+
require 'lhm/sql_helper'
|
14
|
+
|
15
|
+
module IntegrationHelper
|
16
|
+
#
|
17
|
+
# Connectivity
|
18
|
+
#
|
19
|
+
|
20
|
+
def connection
|
21
|
+
ActiveRecord::Base.connection
|
22
|
+
end
|
23
|
+
|
24
|
+
def connect_master!
|
25
|
+
connect!(3306)
|
26
|
+
end
|
27
|
+
|
28
|
+
def connect_slave!
|
29
|
+
connect!(3307)
|
30
|
+
end
|
31
|
+
|
32
|
+
def connect!(port)
|
33
|
+
ActiveRecord::Base.establish_connection(
|
34
|
+
:adapter => defined?(Mysql2) ? 'mysql2' : 'mysql',
|
35
|
+
:host => '127.0.0.1',
|
36
|
+
:database => 'lhm',
|
37
|
+
:username => '',
|
38
|
+
:port => port
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def select_one(*args)
|
43
|
+
connection.select_one(*args)
|
44
|
+
end
|
45
|
+
|
46
|
+
def select_value(*args)
|
47
|
+
connection.select_value(*args)
|
48
|
+
end
|
49
|
+
|
50
|
+
def execute(*args)
|
51
|
+
retries = 10
|
52
|
+
begin
|
53
|
+
connection.execute(*args)
|
54
|
+
rescue ActiveRecord::StatementInvalid => e
|
55
|
+
if (retries -= 1) > 0 && e.message =~ /Table '.*?' doesn't exist/
|
56
|
+
sleep 0.1
|
57
|
+
retry
|
58
|
+
else
|
59
|
+
raise
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def slave(&block)
|
65
|
+
if master_slave_mode?
|
66
|
+
connect_slave!
|
67
|
+
|
68
|
+
# need to wait for the slave to catch up. a better method would be to
|
69
|
+
# check the master binlog position and wait for the slave to catch up
|
70
|
+
# to that position.
|
71
|
+
sleep 1
|
72
|
+
end
|
73
|
+
|
74
|
+
yield block
|
75
|
+
|
76
|
+
if master_slave_mode?
|
77
|
+
connect_master!
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# Test Data
|
83
|
+
#
|
84
|
+
|
85
|
+
def fixture(name)
|
86
|
+
File.read($fixtures.join("#{ name }.ddl"))
|
87
|
+
end
|
88
|
+
|
89
|
+
def table_create(fixture_name)
|
90
|
+
execute "drop table if exists `#{ fixture_name }`"
|
91
|
+
execute fixture(fixture_name)
|
92
|
+
table_read(fixture_name)
|
93
|
+
end
|
94
|
+
|
95
|
+
def table_read(fixture_name)
|
96
|
+
Lhm::Table.parse(fixture_name, connection)
|
97
|
+
end
|
98
|
+
|
99
|
+
def table_exists?(table)
|
100
|
+
connection.table_exists?(table.name)
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Database Helpers
|
105
|
+
#
|
106
|
+
|
107
|
+
def count(table, column, value)
|
108
|
+
query = "select count(*) from #{ table } where #{ column } = '#{ value }'"
|
109
|
+
select_value(query).to_i
|
110
|
+
end
|
111
|
+
|
112
|
+
def count_all(table)
|
113
|
+
query = "select count(*) from #{ table }"
|
114
|
+
select_value(query).to_i
|
115
|
+
end
|
116
|
+
|
117
|
+
def index_on_columns?(table_name, cols, type = :non_unique)
|
118
|
+
key_name = Lhm::SqlHelper.idx_name(table_name, cols)
|
119
|
+
|
120
|
+
index?(table_name, key_name, type)
|
121
|
+
end
|
122
|
+
|
123
|
+
def index?(table_name, key_name, type = :non_unique)
|
124
|
+
non_unique = type == :non_unique ? 1 : 0
|
125
|
+
|
126
|
+
!!select_one(%Q<
|
127
|
+
show indexes in #{ table_name }
|
128
|
+
where key_name = '#{ key_name }'
|
129
|
+
and non_unique = #{ non_unique }
|
130
|
+
>)
|
131
|
+
end
|
132
|
+
|
133
|
+
#
|
134
|
+
# Environment
|
135
|
+
#
|
136
|
+
|
137
|
+
def master_slave_mode?
|
138
|
+
!!ENV["MASTER_SLAVE"]
|
139
|
+
end
|
140
|
+
end
|