myreplicator 0.0.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.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +40 -0
- data/app/assets/javascripts/myreplicator/application.js +21 -0
- data/app/assets/javascripts/myreplicator/chosen.jquery.min.js +10 -0
- data/app/assets/javascripts/myreplicator/cronwtf.js +156 -0
- data/app/assets/javascripts/myreplicator/exports.js +2 -0
- data/app/assets/javascripts/myreplicator/jquery.tipTip.minified.js +21 -0
- data/app/assets/stylesheets/myreplicator/FrancoisOne.ttf +0 -0
- data/app/assets/stylesheets/myreplicator/application.css +544 -0
- data/app/assets/stylesheets/myreplicator/asc-white.gif +0 -0
- data/app/assets/stylesheets/myreplicator/bg.gif +0 -0
- data/app/assets/stylesheets/myreplicator/bg.png +0 -0
- data/app/assets/stylesheets/myreplicator/chosen-sprite.png +0 -0
- data/app/assets/stylesheets/myreplicator/chosen.css +398 -0
- data/app/assets/stylesheets/myreplicator/clipboard-list.png +0 -0
- data/app/assets/stylesheets/myreplicator/cross.png +0 -0
- data/app/assets/stylesheets/myreplicator/desc-white.gif +0 -0
- data/app/assets/stylesheets/myreplicator/exports.css +4 -0
- data/app/assets/stylesheets/myreplicator/gear.png +0 -0
- data/app/assets/stylesheets/myreplicator/plus.png +0 -0
- data/app/assets/stylesheets/myreplicator/status-busy.png +0 -0
- data/app/assets/stylesheets/myreplicator/status.png +0 -0
- data/app/assets/stylesheets/myreplicator/tipTip.css +113 -0
- data/app/assets/stylesheets/myreplicator/websymbols-regular-webfont.eot +0 -0
- data/app/assets/stylesheets/myreplicator/websymbols-regular-webfont.svg +108 -0
- data/app/assets/stylesheets/myreplicator/websymbols-regular-webfont.ttf +0 -0
- data/app/assets/stylesheets/myreplicator/websymbols-regular-webfont.woff +0 -0
- data/app/assets/stylesheets/scaffold.css +56 -0
- data/app/controllers/myreplicator/application_controller.rb +4 -0
- data/app/controllers/myreplicator/exports_controller.rb +106 -0
- data/app/controllers/myreplicator/home_controller.rb +23 -0
- data/app/helpers/myreplicator/application_helper.rb +12 -0
- data/app/helpers/myreplicator/exports_helper.rb +4 -0
- data/app/models/myreplicator/export.rb +121 -0
- data/app/views/layouts/myreplicator/application.html.erb +24 -0
- data/app/views/myreplicator/exports/_form.html.erb +117 -0
- data/app/views/myreplicator/exports/edit.html.erb +2 -0
- data/app/views/myreplicator/exports/index.html.erb +56 -0
- data/app/views/myreplicator/exports/new.html.erb +2 -0
- data/app/views/myreplicator/exports/show.html.erb +28 -0
- data/app/views/myreplicator/home/_home_menu.erb +5 -0
- data/app/views/myreplicator/home/errors.html.erb +10 -0
- data/app/views/myreplicator/home/index.html.erb +58 -0
- data/config/routes.rb +6 -0
- data/db/migrate/20121025191622_create_myreplicator_exports.rb +35 -0
- data/lib/configuration.rb +25 -0
- data/lib/exporter/export_metadata.rb +198 -0
- data/lib/exporter/export_metadata.rb~ +9 -0
- data/lib/exporter/mysql_exporter.rb +187 -0
- data/lib/exporter/sql_commands.rb +126 -0
- data/lib/exporter/sql_commands.rb~ +5 -0
- data/lib/exporter.rb +3 -0
- data/lib/loader/import_sql.rb +91 -0
- data/lib/loader/import_sql.rb~ +27 -0
- data/lib/loader/loader.rb +122 -0
- data/lib/loader/loader.rb~ +4 -0
- data/lib/myreplicator/engine.rb +5 -0
- data/lib/myreplicator/version.rb +3 -0
- data/lib/myreplicator.rb +34 -0
- data/lib/tasks/myreplicator_tasks.rake +4 -0
- data/lib/transporter/parallelizer.rb +78 -0
- data/lib/transporter/transporter.rb +80 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +42 -0
- data/test/dummy/config/database.yml.sample +47 -0
- data/test/dummy/config/database.yml.sample~ +26 -0
- data/test/dummy/config/database.yml~ +35 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/myreplicator.yml +22 -0
- data/test/dummy/config/myreplicator.yml.sample +23 -0
- data/test/dummy/config/myreplicator.yml~ +21 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/migrate/20121101005152_sample_data.rb +24 -0
- data/test/dummy/db/migrate/20121115194022_create_myreplicator_exports.myreplicator.rb +36 -0
- data/test/dummy/db/schema.rb +45 -0
- data/test/dummy/db/seeds.rb +13 -0
- data/test/dummy/db/seeds.rb~ +1 -0
- data/test/dummy/log/development.log +1364 -0
- data/test/dummy/log/test.log +49 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/test/fixtures/myreplicator_exports.yml +0 -0
- data/test/dummy/tmp/cache/assets/C2E/D00/sprockets%2F667019818351638709494c01bddb5f68 +0 -0
- data/test/dummy/tmp/cache/assets/C83/BA0/sprockets%2F701a6339a558e5af28f150c161f43878 +0 -0
- data/test/dummy/tmp/cache/assets/C8B/150/sprockets%2F37163d05e55ad0b31b602ac5330412e3 +0 -0
- data/test/dummy/tmp/cache/assets/CBF/800/sprockets%2F00142a873933017aaa760316d0e2dcea +0 -0
- data/test/dummy/tmp/cache/assets/CC5/870/sprockets%2F4e91734f6f02a779d39f8272f627dd24 +0 -0
- data/test/dummy/tmp/cache/assets/CC9/1C0/sprockets%2Fa7a2b5a56180e1b21f783a9b30c03167 +0 -0
- data/test/dummy/tmp/cache/assets/CD5/B90/sprockets%2Fc999d13a6a21113981c0d820e8043bdf +0 -0
- data/test/dummy/tmp/cache/assets/CD7/030/sprockets%2F9ba4859590582b8b72a650b2b00b6cd2 +0 -0
- data/test/dummy/tmp/cache/assets/CDE/780/sprockets%2F6982ce9303b4e69c26f2a1a246a180e7 +0 -0
- data/test/dummy/tmp/cache/assets/CE5/670/sprockets%2Fe9e4122f1706626a21da6f8457f088ce +0 -0
- data/test/dummy/tmp/cache/assets/CF7/820/sprockets%2F6284656df87a7eb2545ed9b957560a7b +0 -0
- data/test/dummy/tmp/cache/assets/D00/9B0/sprockets%2F2bc203eb4802b393a589093debb65c04 +0 -0
- data/test/dummy/tmp/cache/assets/D00/A90/sprockets%2F3fa6fcf2205c530208681446cbc36bd3 +0 -0
- data/test/dummy/tmp/cache/assets/D0B/B30/sprockets%2F55059589d999952597c5c86e5becaf4e +0 -0
- data/test/dummy/tmp/cache/assets/D0D/DA0/sprockets%2F34d6b075a16a5a58ff4050988d8bd4b0 +0 -0
- data/test/dummy/tmp/cache/assets/D12/B40/sprockets%2F9c825562fe2a2a38bd6f41a635265bd9 +0 -0
- data/test/dummy/tmp/cache/assets/D1B/D40/sprockets%2F7cc2142509e95f7168a82a652d8d9dea +0 -0
- data/test/dummy/tmp/cache/assets/D1D/700/sprockets%2Ffe9cb975216709e2881c74b3d1d3e35f +0 -0
- data/test/dummy/tmp/cache/assets/D20/A20/sprockets%2Fb503e93ff1966dd94d03e79f291d75c1 +0 -0
- data/test/dummy/tmp/cache/assets/D26/A70/sprockets%2F63d95ae156df465783cd78e95069b2cc +0 -0
- data/test/dummy/tmp/cache/assets/D2B/E60/sprockets%2F8615ecf645b553c959544af9ff8438da +0 -0
- data/test/dummy/tmp/cache/assets/D34/F70/sprockets%2Fb93de9992473bee94e369fe3198c529c +0 -0
- data/test/dummy/tmp/cache/assets/D36/4D0/sprockets%2Fc050a64b5a803a638e155d05dcfe577d +0 -0
- data/test/dummy/tmp/cache/assets/D3E/310/sprockets%2F333a2a7535eac766267ebb7d5c2ab489 +0 -0
- data/test/dummy/tmp/cache/assets/D3F/A00/sprockets%2F7a803404e1f60b8d672d763cb9ba8af5 +0 -0
- data/test/dummy/tmp/cache/assets/D50/570/sprockets%2F6c1d20a178f66e798958d1e437fdb5da +0 -0
- data/test/dummy/tmp/cache/assets/D57/420/sprockets%2F937ea7429c536578ec7b688dee0cdf41 +0 -0
- data/test/dummy/tmp/cache/assets/D69/6F0/sprockets%2F94fff7f55bc4c300b25f3f9361ac1a52 +0 -0
- data/test/dummy/tmp/cache/assets/D86/D00/sprockets%2Fa4f32b4234d0d1bba272cd75e0d48e1d +0 -0
- data/test/dummy/tmp/cache/assets/D8B/B60/sprockets%2Faa32227c440a378ccd21218eefeb80bf +0 -0
- data/test/dummy/tmp/cache/assets/D9F/0B0/sprockets%2Faf0d2e69be3a6b56a76c20bf14d9e468 +0 -0
- data/test/dummy/tmp/cache/assets/DA8/910/sprockets%2Fab5775c4a837bd4d97ac394d473cda9b +0 -0
- data/test/dummy/tmp/cache/assets/DC0/100/sprockets%2F7a5d4f0d352bceed0dce0449c82251bd +0 -0
- data/test/dummy/tmp/cache/assets/DD2/490/sprockets%2Fa452ee92a092bc2feabc572cce49896d +0 -0
- data/test/dummy/tmp/cache/assets/DE1/320/sprockets%2F9f44ecdec8ceeef70871e15d88a448b1 +0 -0
- data/test/dummy/tmp/cache/assets/DF8/5D0/sprockets%2Fb815ed34d61cfed96222daa3bfd1d84d +0 -0
- data/test/dummy/tmp/cache/assets/E16/C70/sprockets%2F21ad93418ff75ceac86c7a85dfffe8f6 +0 -0
- data/test/dummy/tmp/cache/assets/E35/4F0/sprockets%2F96b1cdf8db6a1c8eb8abcce05958ae74 +0 -0
- data/test/dummy/tmp/cache/assets/E4E/300/sprockets%2Fefaae6e1a19a7c8f3acebdd5a36a6103 +0 -0
- data/test/fixtures/myreplicator/exports.yml +11 -0
- data/test/functional/myreplicator/exports_controller_test.rb +51 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/myreplicator_test.rb +7 -0
- data/test/test_helper.rb +15 -0
- data/test/unit/helpers/myreplicator/exports_helper_test.rb +6 -0
- data/test/unit/myreplicator/export_test.rb +10 -0
- data/test/unit/myreplicator/exporter_test.rb +11 -0
- data/test/unit/myreplicator/exporter_test.rb~ +10 -0
- metadata +410 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
module Myreplicator
|
|
2
|
+
module SqlCommands
|
|
3
|
+
|
|
4
|
+
def self.mysqldump *args
|
|
5
|
+
options = args.extract_options!
|
|
6
|
+
options.reverse_merge! :flags => []
|
|
7
|
+
db = options[:db]
|
|
8
|
+
|
|
9
|
+
flags = ""
|
|
10
|
+
|
|
11
|
+
self.dump_flags.each_pair do |flag, value|
|
|
12
|
+
if options[:flags].include? flag
|
|
13
|
+
flags += " --#{flag} "
|
|
14
|
+
elsif value
|
|
15
|
+
flags += " --#{flag} "
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Database host when ssh'ed into the db server
|
|
20
|
+
db_host = ssh_configs(db)["ssh_db_host"].nil? ? "127.0.0.1" : ssh_configs(db)["ssh_db_host"]
|
|
21
|
+
|
|
22
|
+
cmd = Myreplicator.mysqldump
|
|
23
|
+
cmd += "#{flags} -u#{db_configs(db)["username"]} -p#{db_configs(db)["password"]} "
|
|
24
|
+
cmd += "-h#{db_host} " if db_configs(db)["host"]
|
|
25
|
+
cmd += " -P#{db_configs(db)["port"]} " if db_configs(db)["port"]
|
|
26
|
+
cmd += " #{db} "
|
|
27
|
+
cmd += " #{options[:table_name]} "
|
|
28
|
+
cmd += "--result-file=#{options[:filepath]} "
|
|
29
|
+
|
|
30
|
+
# cmd += "--tab=#{options[:filepath]} "
|
|
31
|
+
# cmd += "--fields-enclosed-by=\'\"\' "
|
|
32
|
+
# cmd += "--fields-escaped-by=\'\\\\\' "
|
|
33
|
+
|
|
34
|
+
return cmd
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.db_configs db
|
|
38
|
+
ActiveRecord::Base.configurations[db]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.ssh_configs db
|
|
42
|
+
Myreplicator.configs[db]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.dump_flags
|
|
46
|
+
{"add-locks" => true,
|
|
47
|
+
"compact" => false,
|
|
48
|
+
"lock-tables" => false,
|
|
49
|
+
"no-create-db" => true,
|
|
50
|
+
"no-data" => false,
|
|
51
|
+
"quick" => true,
|
|
52
|
+
"skip-add-drop-table" => false,
|
|
53
|
+
"create-options" => false,
|
|
54
|
+
"single-transaction" => false
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.mysql_export *args
|
|
59
|
+
options = args.extract_options!
|
|
60
|
+
options.reverse_merge! :flags => []
|
|
61
|
+
db = options[:db]
|
|
62
|
+
# Database host when ssh'ed into the db server
|
|
63
|
+
db_host = ssh_configs(db)["ssh_db_host"].nil? ? "127.0.0.1" : ssh_configs(db)["ssh_db_host"]
|
|
64
|
+
|
|
65
|
+
flags = ""
|
|
66
|
+
|
|
67
|
+
self.mysql_flags.each_pair do |flag, value|
|
|
68
|
+
if options[:flags].include? flag
|
|
69
|
+
flags += " --#{flag} "
|
|
70
|
+
elsif value
|
|
71
|
+
flags += " --#{flag} "
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
cmd = Myreplicator.mysql
|
|
76
|
+
cmd += "#{flags} -u#{db_configs(db)["username"]} -p#{db_configs(db)["password"]} "
|
|
77
|
+
cmd += "-h#{db_host} " if db_configs(db)["host"].blank?
|
|
78
|
+
cmd += db_configs(db)["port"].blank? ? "-P3306 " : "-P#{db_configs(db)["port"]} "
|
|
79
|
+
cmd += "--execute=\"#{options[:sql]}\" "
|
|
80
|
+
cmd += " > #{options[:filepath]} "
|
|
81
|
+
|
|
82
|
+
puts cmd
|
|
83
|
+
return cmd
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.mysql_flags
|
|
87
|
+
{"column-names" => false,
|
|
88
|
+
"quick" => true,
|
|
89
|
+
"reconnect" => true
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def self.export_sql *args
|
|
94
|
+
options = args.extract_options!
|
|
95
|
+
sql = "SELECT * FROM #{options[:db]}.#{options[:table]} "
|
|
96
|
+
|
|
97
|
+
if options[:incremental_col] && options[:incremental_val]
|
|
98
|
+
if options[:incremental_col_type] == "datetime"
|
|
99
|
+
sql += "WHERE #{options[:incremental_col]} >= '#{options[:incremental_val]}'"
|
|
100
|
+
else
|
|
101
|
+
sql += "WHERE #{options[:incremental_col]} >= #{options[:incremental_val]}"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
return sql
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def self.max_value_sql *args
|
|
109
|
+
options = args.extract_options!
|
|
110
|
+
sql = ""
|
|
111
|
+
|
|
112
|
+
if options[:incremental_col]
|
|
113
|
+
sql = "SELECT max(#{options[:incremental_col]}) FROM #{options[:db]}.#{options[:table]}"
|
|
114
|
+
else
|
|
115
|
+
raise Myreplicator::Exceptions::MissingArgs.new("Missing Incremental Column Parameter")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
return sql
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def self.mysql_export_outfile
|
|
122
|
+
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
end
|
|
126
|
+
end
|
data/lib/exporter.rb
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Dir["#{File.expand_path(File.dirname(__FILE__))}/exporter/*.rb"].each { | f | require File.expand_path(f) }
|
|
2
|
+
Dir["#{File.expand_path(File.dirname(__FILE__))}/transporter/*.rb"].each { | f | require File.expand_path(f) }
|
|
3
|
+
Dir["#{File.expand_path(File.dirname(__FILE__))}/loader/*.rb"].each { | f | require File.expand_path(f) }
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Myreplicator
|
|
2
|
+
class ImportSql
|
|
3
|
+
class << self
|
|
4
|
+
|
|
5
|
+
def load_data_infile *args
|
|
6
|
+
options = args.extract_options!
|
|
7
|
+
sql = build_load_data_infile options
|
|
8
|
+
|
|
9
|
+
cmd = mysql_cmd(options[:db])
|
|
10
|
+
cmd += " --local_infile=1 " # Used for mysql versions that do not support -e and LDI
|
|
11
|
+
cmd += "-e \"#{sql}\" "
|
|
12
|
+
|
|
13
|
+
return cmd
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def build_load_data_infile options
|
|
17
|
+
options.reverse_merge!(:replace => true,
|
|
18
|
+
:fields_terminated_by => "\\t",
|
|
19
|
+
:lines_terminated_by => "\\n"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
handle = options[:replace] ? 'REPLACE' : 'IGNORE'
|
|
24
|
+
|
|
25
|
+
sql = "LOAD DATA LOCAL INFILE '#{options[:filepath]}' #{handle} "
|
|
26
|
+
sql += "INTO TABLE #{options[:db]}.#{options[:table_name]} "
|
|
27
|
+
|
|
28
|
+
if options.include?(:character_set)
|
|
29
|
+
sql << " CHARACTER SET #{options[:character_set]}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
if options.include?(:fields_terminated_by) or options.include?(:enclosed_by) or options.include?(:escaped_by)
|
|
33
|
+
sql << " FIELDS"
|
|
34
|
+
end
|
|
35
|
+
if options.include?(:fields_terminated_by)
|
|
36
|
+
sql << " TERMINATED BY '#{options[:fields_terminated_by]}'"
|
|
37
|
+
end
|
|
38
|
+
if options.include?(:enclosed_by)
|
|
39
|
+
sql << " ENCLOSED BY '#{options[:enclosed_by]}'"
|
|
40
|
+
end
|
|
41
|
+
if options.include?(:escaped_by)
|
|
42
|
+
sql << " ESCAPED BY '#{options[:escaped_by]}'"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if options.include?(:starting_by) or options.include?(:lines_terminated_by)
|
|
46
|
+
sql << " LINES"
|
|
47
|
+
end
|
|
48
|
+
if options.include?(:starting_by)
|
|
49
|
+
sql << " STARTING BY '#{options[:starting_by]}'"
|
|
50
|
+
end
|
|
51
|
+
if options.include?(:lines_terminated_by)
|
|
52
|
+
sql << " TERMINATED BY '#{options[:lines_terminated_by]}'"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if options.include?(:ignore)
|
|
56
|
+
sql << " IGNORE #{options[:ignore]} LINES"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if options.include?(:fields) and !options[:fields].empty?
|
|
60
|
+
sql << "( #{options[:fields].join(', ')} )"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
return sql
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def initial_load *args
|
|
67
|
+
options = args.extract_options!
|
|
68
|
+
|
|
69
|
+
cmd = mysql_cmd(options[:db])
|
|
70
|
+
cmd += " #{options[:db]} "
|
|
71
|
+
cmd += " < #{options[:filepath]} "
|
|
72
|
+
|
|
73
|
+
return cmd
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def mysql_cmd db
|
|
77
|
+
# Destination database host
|
|
78
|
+
db_host = SqlCommands.db_configs(db).has_key?("host") ? SqlCommands.db_configs(db)["host"] : "127.0.0.1"
|
|
79
|
+
|
|
80
|
+
cmd = Myreplicator.mysql
|
|
81
|
+
cmd += " -u#{SqlCommands.db_configs(db)["username"]} -p#{SqlCommands.db_configs(db)["password"]} "
|
|
82
|
+
cmd += " -h#{db_host} "
|
|
83
|
+
cmd += " -P#{SqlCommands.db_configs(db)["port"]} " if SqlCommands.db_configs(db)["port"]
|
|
84
|
+
|
|
85
|
+
return cmd
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end #self
|
|
89
|
+
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Myreplicator
|
|
2
|
+
module ImportSql
|
|
3
|
+
|
|
4
|
+
def self.load_data_infile *args
|
|
5
|
+
options = args.extract_options!
|
|
6
|
+
sql = ""
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.initial_load *args
|
|
10
|
+
options = args.extract_options!
|
|
11
|
+
db = options[:db]
|
|
12
|
+
cmd = ""
|
|
13
|
+
|
|
14
|
+
# Destination database host
|
|
15
|
+
db_host = db_configs(db).has_key?("host") ? db_configs(db)["host"] : "127.0.0.1"
|
|
16
|
+
|
|
17
|
+
cmd = Myreplicator.mysql
|
|
18
|
+
cmd += "-u#{db_configs(db)["username"]} -p#{db_configs(db)["password"]} "
|
|
19
|
+
cmd += "-h#{db_host} "
|
|
20
|
+
cmd += " -P#{db_configs(db)["port"]} " if db_configs(db)["port"]
|
|
21
|
+
cmd += " #{db} "
|
|
22
|
+
cmd += " #{options[:table_name]} "
|
|
23
|
+
cmd += " < #{options[:filepath]} "
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
require "exporter"
|
|
2
|
+
|
|
3
|
+
module Myreplicator
|
|
4
|
+
class Loader
|
|
5
|
+
|
|
6
|
+
def initialize *args
|
|
7
|
+
options = args.extract_options!
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def tmp_dir
|
|
11
|
+
@tmp_dir ||= File.join(Myreplicator.app_root,"tmp", "myreplicator")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def load
|
|
15
|
+
initials = []
|
|
16
|
+
incrementals = []
|
|
17
|
+
|
|
18
|
+
metadata_files.each do |metadata|
|
|
19
|
+
if metadata.export_type == "initial"
|
|
20
|
+
initials << metadata
|
|
21
|
+
else
|
|
22
|
+
incrementals << metadata
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
initials.each do |metadata|
|
|
27
|
+
puts metadata.table
|
|
28
|
+
initial_load metadata
|
|
29
|
+
cleanup metadata
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
incrementals.each do |metadata|
|
|
33
|
+
puts metadata.table
|
|
34
|
+
incremental_load metadata
|
|
35
|
+
cleanup metadata
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Creates table and loads data
|
|
41
|
+
##
|
|
42
|
+
def initial_load metadata
|
|
43
|
+
exp = Export.find(metadata.export_id)
|
|
44
|
+
unzip(metadata.filename)
|
|
45
|
+
metadata.zipped = false
|
|
46
|
+
|
|
47
|
+
cmd = ImportSql.initial_load(:db => exp.destination_schema,
|
|
48
|
+
:filepath => metadata.destination_filepath(tmp_dir))
|
|
49
|
+
puts cmd
|
|
50
|
+
result = `#{cmd}` # execute
|
|
51
|
+
unless result.nil?
|
|
52
|
+
if result.size > 0
|
|
53
|
+
raise Exceptions::LoaderError.new("Initial Load #{metadata.filename} Failed!\n#{result}")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
##
|
|
59
|
+
# Loads data incrementally
|
|
60
|
+
# Uses the values specified in the metadatta object
|
|
61
|
+
##
|
|
62
|
+
def incremental_load metadata
|
|
63
|
+
exp = Export.find(metadata.export_id)
|
|
64
|
+
unzip(metadata.filename)
|
|
65
|
+
metadata.zipped = false
|
|
66
|
+
|
|
67
|
+
cmd = ImportSql.load_data_infile(:table_name => exp.table_name,
|
|
68
|
+
:db => exp.destination_schema,
|
|
69
|
+
:filepath => metadata.destination_filepath(tmp_dir))
|
|
70
|
+
puts cmd
|
|
71
|
+
result = `#{cmd}` # execute
|
|
72
|
+
unless result.nil?
|
|
73
|
+
if result.size > 0
|
|
74
|
+
raise Exceptions::LoaderError.new("Incremental Load #{metadata.filename} Failed!\n#{result}")
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
##
|
|
80
|
+
# Deletes the metadata file and extract
|
|
81
|
+
##
|
|
82
|
+
def cleanup metadata
|
|
83
|
+
puts "Cleaning up..."
|
|
84
|
+
FileUtils.rm "#{metadata.destination_filepath(tmp_dir)}.json" # json file
|
|
85
|
+
FileUtils.rm metadata.destination_filepath(tmp_dir) # dump file
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
##
|
|
89
|
+
# Unzips file
|
|
90
|
+
# Checks if the file exists or already unzipped
|
|
91
|
+
##
|
|
92
|
+
def unzip filename
|
|
93
|
+
cmd = "cd #{tmp_dir}; gunzip #{filename}"
|
|
94
|
+
passed = false
|
|
95
|
+
if File.exist?(File.join(tmp_dir,filename))
|
|
96
|
+
result = `#{cmd}`
|
|
97
|
+
unless result.nil?
|
|
98
|
+
puts result
|
|
99
|
+
unless result.length > 0
|
|
100
|
+
passed = true
|
|
101
|
+
end
|
|
102
|
+
else
|
|
103
|
+
passed = true
|
|
104
|
+
end
|
|
105
|
+
elsif File.exist?(File.join(tmp_dir,filename.gsub(".gz","")))
|
|
106
|
+
puts "File already unzipped"
|
|
107
|
+
passed = true
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
raise Exceptions::LoaderError.new("Unzipping #{filename} Failed!") unless passed
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def metadata_files
|
|
114
|
+
files = []
|
|
115
|
+
Dir.glob(File.join(tmp_dir, "*.json")).each do |json_file|
|
|
116
|
+
files << ExportMetadata.new(:metadata_path => json_file)
|
|
117
|
+
end
|
|
118
|
+
return files
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
end
|
|
122
|
+
end
|
data/lib/myreplicator.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require "myreplicator/engine"
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
module Myreplicator
|
|
5
|
+
mattr_accessor :app_root, :tmp_path, :mysql, :mysqldump, :configs
|
|
6
|
+
|
|
7
|
+
class Engine < Rails::Engine
|
|
8
|
+
|
|
9
|
+
initializer "myreplicator.configure_rails_initialization" do |app|
|
|
10
|
+
Myreplicator.app_root = app.root #assigning app root as rails.root is not accessible
|
|
11
|
+
yml = YAML.load(File.read("#{Myreplicator.app_root}/config/myreplicator.yml"))
|
|
12
|
+
|
|
13
|
+
Myreplicator.mysql = yml["myreplicator"]["mysql"]
|
|
14
|
+
Myreplicator.mysqldump = yml["myreplicator"]["mysqldump"]
|
|
15
|
+
|
|
16
|
+
if yml["myreplicator"]["tmp_path"].blank?
|
|
17
|
+
Myreplicator.tmp_path = File.join(Myreplicator.app_root, "tmp", "myreplicator")
|
|
18
|
+
else
|
|
19
|
+
Myreplicator.tmp_path = yml["myreplicator"]["tmp_path"]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Myreplicator.configs = yml
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module Exceptions
|
|
28
|
+
class MissingArgs < StandardError; end
|
|
29
|
+
class ExportError < StandardError; end
|
|
30
|
+
class LoaderError < StandardError; end
|
|
31
|
+
class ExportIgnored < StandardError; end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require "thread"
|
|
2
|
+
|
|
3
|
+
module Myreplicator
|
|
4
|
+
class Parallelizer
|
|
5
|
+
|
|
6
|
+
attr_accessor :queue
|
|
7
|
+
|
|
8
|
+
def initialize *args
|
|
9
|
+
options = args.extract_options!
|
|
10
|
+
@queue = Queue.new
|
|
11
|
+
@threads = []
|
|
12
|
+
@max_threads = options[:max_threads].nil? ? 10 : options[:max_threads]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# Runs while there are jobs in the queue
|
|
17
|
+
# Waits for a second and checks for available threads
|
|
18
|
+
# Exits when all jobs are allocated in threads
|
|
19
|
+
##
|
|
20
|
+
def run
|
|
21
|
+
@done = false
|
|
22
|
+
@manager_running = false
|
|
23
|
+
|
|
24
|
+
while @queue.size > 0
|
|
25
|
+
if @threads.size <= @max_threads
|
|
26
|
+
@threads << Thread.new(@queue.pop) do |proc|
|
|
27
|
+
Thread.current[:status] = 'running' # Manually Set Thread state for Checks
|
|
28
|
+
Transporter.new.instance_exec(proc[:params], &proc[:block])
|
|
29
|
+
Thread.current[:status] = 'done'
|
|
30
|
+
end
|
|
31
|
+
else
|
|
32
|
+
unless @manager_running
|
|
33
|
+
manage_threads
|
|
34
|
+
@manager_running = true
|
|
35
|
+
end
|
|
36
|
+
sleep 1
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
manage_threads unless @manager_running
|
|
41
|
+
|
|
42
|
+
# Waits until all threads are completed
|
|
43
|
+
# Before exiting
|
|
44
|
+
while !@done
|
|
45
|
+
sleep 1
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# Clears dead threads,
|
|
52
|
+
# frees thread pool for more jobs
|
|
53
|
+
# Exits when no more threads are left
|
|
54
|
+
##
|
|
55
|
+
def manage_threads
|
|
56
|
+
Thread.new do
|
|
57
|
+
while(@threads.size > 0)
|
|
58
|
+
done = []
|
|
59
|
+
@threads.each do |t|
|
|
60
|
+
done << t if t[:status] == "done"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
done.each{|d| @threads.delete(d)} # Clear dead threads
|
|
64
|
+
|
|
65
|
+
# If no more jobs are left, mark done
|
|
66
|
+
|
|
67
|
+
if @queue.size == 0 && @threads.size == 0
|
|
68
|
+
@done = true
|
|
69
|
+
else
|
|
70
|
+
sleep 2 # Wait for more threads to spawn
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require "exporter"
|
|
2
|
+
|
|
3
|
+
module Myreplicator
|
|
4
|
+
class Transporter
|
|
5
|
+
|
|
6
|
+
def initialize *args
|
|
7
|
+
options = args.extract_options!
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def tmp_dir
|
|
11
|
+
@tmp_dir ||= File.join(Myreplicator.app_root,"tmp", "myreplicator")
|
|
12
|
+
Dir.mkdir(@tmp_dir) unless File.directory?(@tmp_dir)
|
|
13
|
+
@tmp_dir
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def transfer
|
|
17
|
+
unique_jobs = Export.where("state != 'failed' and active = 1").group("source_schema")
|
|
18
|
+
|
|
19
|
+
unique_jobs.each do |export|
|
|
20
|
+
download export
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def download export
|
|
25
|
+
ssh = export.ssh_to_source
|
|
26
|
+
parallel_download(export, ssh, completed_files(ssh, export))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def parallel_download export, ssh, files
|
|
30
|
+
p = Parallelizer.new
|
|
31
|
+
|
|
32
|
+
files.each do |filename|
|
|
33
|
+
puts filename
|
|
34
|
+
p.queue << {:params =>[ssh, export, filename], :block => download_file}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
p.run
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def download_file
|
|
41
|
+
proc = Proc.new { |params|
|
|
42
|
+
ssh = params[0]
|
|
43
|
+
export = params[1]
|
|
44
|
+
filename = params[2]
|
|
45
|
+
sftp = export.sftp_to_source
|
|
46
|
+
json_file = remote_path(export, filename)
|
|
47
|
+
json_local_path = File.join(tmp_dir,filename)
|
|
48
|
+
puts "Downloading #{json_file}"
|
|
49
|
+
sftp.download!(json_file, json_local_path)
|
|
50
|
+
dump_file = get_dump_path(json_local_path)
|
|
51
|
+
puts "Downloading #{dump_file}"
|
|
52
|
+
sftp.download!(dump_file, File.join(tmp_dir, dump_file.split("/").last))
|
|
53
|
+
}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def completed_files ssh, export
|
|
57
|
+
done_files = ssh.exec!(get_done_files(export))
|
|
58
|
+
|
|
59
|
+
unless done_files.blank?
|
|
60
|
+
return done_files.split("\n")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
return []
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def get_dump_path json_path
|
|
67
|
+
metadata = ExportMetadata.new(:metadata_path => json_path)
|
|
68
|
+
return metadata.export_path
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def remote_path export, filename
|
|
72
|
+
File.join(Myreplicator.configs[export.source_schema]["ssh_tmp_dir"], filename)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def get_done_files export
|
|
76
|
+
cmd = "cd #{Myreplicator.configs[export.source_schema]["ssh_tmp_dir"]}; grep -l export_completed *.json"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
end
|