myreplicator 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|