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.
Files changed (154) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +3 -0
  3. data/Rakefile +40 -0
  4. data/app/assets/javascripts/myreplicator/application.js +21 -0
  5. data/app/assets/javascripts/myreplicator/chosen.jquery.min.js +10 -0
  6. data/app/assets/javascripts/myreplicator/cronwtf.js +156 -0
  7. data/app/assets/javascripts/myreplicator/exports.js +2 -0
  8. data/app/assets/javascripts/myreplicator/jquery.tipTip.minified.js +21 -0
  9. data/app/assets/stylesheets/myreplicator/FrancoisOne.ttf +0 -0
  10. data/app/assets/stylesheets/myreplicator/application.css +544 -0
  11. data/app/assets/stylesheets/myreplicator/asc-white.gif +0 -0
  12. data/app/assets/stylesheets/myreplicator/bg.gif +0 -0
  13. data/app/assets/stylesheets/myreplicator/bg.png +0 -0
  14. data/app/assets/stylesheets/myreplicator/chosen-sprite.png +0 -0
  15. data/app/assets/stylesheets/myreplicator/chosen.css +398 -0
  16. data/app/assets/stylesheets/myreplicator/clipboard-list.png +0 -0
  17. data/app/assets/stylesheets/myreplicator/cross.png +0 -0
  18. data/app/assets/stylesheets/myreplicator/desc-white.gif +0 -0
  19. data/app/assets/stylesheets/myreplicator/exports.css +4 -0
  20. data/app/assets/stylesheets/myreplicator/gear.png +0 -0
  21. data/app/assets/stylesheets/myreplicator/plus.png +0 -0
  22. data/app/assets/stylesheets/myreplicator/status-busy.png +0 -0
  23. data/app/assets/stylesheets/myreplicator/status.png +0 -0
  24. data/app/assets/stylesheets/myreplicator/tipTip.css +113 -0
  25. data/app/assets/stylesheets/myreplicator/websymbols-regular-webfont.eot +0 -0
  26. data/app/assets/stylesheets/myreplicator/websymbols-regular-webfont.svg +108 -0
  27. data/app/assets/stylesheets/myreplicator/websymbols-regular-webfont.ttf +0 -0
  28. data/app/assets/stylesheets/myreplicator/websymbols-regular-webfont.woff +0 -0
  29. data/app/assets/stylesheets/scaffold.css +56 -0
  30. data/app/controllers/myreplicator/application_controller.rb +4 -0
  31. data/app/controllers/myreplicator/exports_controller.rb +106 -0
  32. data/app/controllers/myreplicator/home_controller.rb +23 -0
  33. data/app/helpers/myreplicator/application_helper.rb +12 -0
  34. data/app/helpers/myreplicator/exports_helper.rb +4 -0
  35. data/app/models/myreplicator/export.rb +121 -0
  36. data/app/views/layouts/myreplicator/application.html.erb +24 -0
  37. data/app/views/myreplicator/exports/_form.html.erb +117 -0
  38. data/app/views/myreplicator/exports/edit.html.erb +2 -0
  39. data/app/views/myreplicator/exports/index.html.erb +56 -0
  40. data/app/views/myreplicator/exports/new.html.erb +2 -0
  41. data/app/views/myreplicator/exports/show.html.erb +28 -0
  42. data/app/views/myreplicator/home/_home_menu.erb +5 -0
  43. data/app/views/myreplicator/home/errors.html.erb +10 -0
  44. data/app/views/myreplicator/home/index.html.erb +58 -0
  45. data/config/routes.rb +6 -0
  46. data/db/migrate/20121025191622_create_myreplicator_exports.rb +35 -0
  47. data/lib/configuration.rb +25 -0
  48. data/lib/exporter/export_metadata.rb +198 -0
  49. data/lib/exporter/export_metadata.rb~ +9 -0
  50. data/lib/exporter/mysql_exporter.rb +187 -0
  51. data/lib/exporter/sql_commands.rb +126 -0
  52. data/lib/exporter/sql_commands.rb~ +5 -0
  53. data/lib/exporter.rb +3 -0
  54. data/lib/loader/import_sql.rb +91 -0
  55. data/lib/loader/import_sql.rb~ +27 -0
  56. data/lib/loader/loader.rb +122 -0
  57. data/lib/loader/loader.rb~ +4 -0
  58. data/lib/myreplicator/engine.rb +5 -0
  59. data/lib/myreplicator/version.rb +3 -0
  60. data/lib/myreplicator.rb +34 -0
  61. data/lib/tasks/myreplicator_tasks.rake +4 -0
  62. data/lib/transporter/parallelizer.rb +78 -0
  63. data/lib/transporter/transporter.rb +80 -0
  64. data/test/dummy/README.rdoc +261 -0
  65. data/test/dummy/Rakefile +7 -0
  66. data/test/dummy/app/assets/javascripts/application.js +15 -0
  67. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  68. data/test/dummy/app/controllers/application_controller.rb +3 -0
  69. data/test/dummy/app/helpers/application_helper.rb +2 -0
  70. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  71. data/test/dummy/config/application.rb +59 -0
  72. data/test/dummy/config/boot.rb +10 -0
  73. data/test/dummy/config/database.yml +42 -0
  74. data/test/dummy/config/database.yml.sample +47 -0
  75. data/test/dummy/config/database.yml.sample~ +26 -0
  76. data/test/dummy/config/database.yml~ +35 -0
  77. data/test/dummy/config/environment.rb +5 -0
  78. data/test/dummy/config/environments/development.rb +37 -0
  79. data/test/dummy/config/environments/production.rb +67 -0
  80. data/test/dummy/config/environments/test.rb +37 -0
  81. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  82. data/test/dummy/config/initializers/inflections.rb +15 -0
  83. data/test/dummy/config/initializers/mime_types.rb +5 -0
  84. data/test/dummy/config/initializers/secret_token.rb +7 -0
  85. data/test/dummy/config/initializers/session_store.rb +8 -0
  86. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  87. data/test/dummy/config/locales/en.yml +5 -0
  88. data/test/dummy/config/myreplicator.yml +22 -0
  89. data/test/dummy/config/myreplicator.yml.sample +23 -0
  90. data/test/dummy/config/myreplicator.yml~ +21 -0
  91. data/test/dummy/config/routes.rb +4 -0
  92. data/test/dummy/config.ru +4 -0
  93. data/test/dummy/db/migrate/20121101005152_sample_data.rb +24 -0
  94. data/test/dummy/db/migrate/20121115194022_create_myreplicator_exports.myreplicator.rb +36 -0
  95. data/test/dummy/db/schema.rb +45 -0
  96. data/test/dummy/db/seeds.rb +13 -0
  97. data/test/dummy/db/seeds.rb~ +1 -0
  98. data/test/dummy/log/development.log +1364 -0
  99. data/test/dummy/log/test.log +49 -0
  100. data/test/dummy/public/404.html +26 -0
  101. data/test/dummy/public/422.html +26 -0
  102. data/test/dummy/public/500.html +25 -0
  103. data/test/dummy/public/favicon.ico +0 -0
  104. data/test/dummy/script/rails +6 -0
  105. data/test/dummy/test/fixtures/myreplicator_exports.yml +0 -0
  106. data/test/dummy/tmp/cache/assets/C2E/D00/sprockets%2F667019818351638709494c01bddb5f68 +0 -0
  107. data/test/dummy/tmp/cache/assets/C83/BA0/sprockets%2F701a6339a558e5af28f150c161f43878 +0 -0
  108. data/test/dummy/tmp/cache/assets/C8B/150/sprockets%2F37163d05e55ad0b31b602ac5330412e3 +0 -0
  109. data/test/dummy/tmp/cache/assets/CBF/800/sprockets%2F00142a873933017aaa760316d0e2dcea +0 -0
  110. data/test/dummy/tmp/cache/assets/CC5/870/sprockets%2F4e91734f6f02a779d39f8272f627dd24 +0 -0
  111. data/test/dummy/tmp/cache/assets/CC9/1C0/sprockets%2Fa7a2b5a56180e1b21f783a9b30c03167 +0 -0
  112. data/test/dummy/tmp/cache/assets/CD5/B90/sprockets%2Fc999d13a6a21113981c0d820e8043bdf +0 -0
  113. data/test/dummy/tmp/cache/assets/CD7/030/sprockets%2F9ba4859590582b8b72a650b2b00b6cd2 +0 -0
  114. data/test/dummy/tmp/cache/assets/CDE/780/sprockets%2F6982ce9303b4e69c26f2a1a246a180e7 +0 -0
  115. data/test/dummy/tmp/cache/assets/CE5/670/sprockets%2Fe9e4122f1706626a21da6f8457f088ce +0 -0
  116. data/test/dummy/tmp/cache/assets/CF7/820/sprockets%2F6284656df87a7eb2545ed9b957560a7b +0 -0
  117. data/test/dummy/tmp/cache/assets/D00/9B0/sprockets%2F2bc203eb4802b393a589093debb65c04 +0 -0
  118. data/test/dummy/tmp/cache/assets/D00/A90/sprockets%2F3fa6fcf2205c530208681446cbc36bd3 +0 -0
  119. data/test/dummy/tmp/cache/assets/D0B/B30/sprockets%2F55059589d999952597c5c86e5becaf4e +0 -0
  120. data/test/dummy/tmp/cache/assets/D0D/DA0/sprockets%2F34d6b075a16a5a58ff4050988d8bd4b0 +0 -0
  121. data/test/dummy/tmp/cache/assets/D12/B40/sprockets%2F9c825562fe2a2a38bd6f41a635265bd9 +0 -0
  122. data/test/dummy/tmp/cache/assets/D1B/D40/sprockets%2F7cc2142509e95f7168a82a652d8d9dea +0 -0
  123. data/test/dummy/tmp/cache/assets/D1D/700/sprockets%2Ffe9cb975216709e2881c74b3d1d3e35f +0 -0
  124. data/test/dummy/tmp/cache/assets/D20/A20/sprockets%2Fb503e93ff1966dd94d03e79f291d75c1 +0 -0
  125. data/test/dummy/tmp/cache/assets/D26/A70/sprockets%2F63d95ae156df465783cd78e95069b2cc +0 -0
  126. data/test/dummy/tmp/cache/assets/D2B/E60/sprockets%2F8615ecf645b553c959544af9ff8438da +0 -0
  127. data/test/dummy/tmp/cache/assets/D34/F70/sprockets%2Fb93de9992473bee94e369fe3198c529c +0 -0
  128. data/test/dummy/tmp/cache/assets/D36/4D0/sprockets%2Fc050a64b5a803a638e155d05dcfe577d +0 -0
  129. data/test/dummy/tmp/cache/assets/D3E/310/sprockets%2F333a2a7535eac766267ebb7d5c2ab489 +0 -0
  130. data/test/dummy/tmp/cache/assets/D3F/A00/sprockets%2F7a803404e1f60b8d672d763cb9ba8af5 +0 -0
  131. data/test/dummy/tmp/cache/assets/D50/570/sprockets%2F6c1d20a178f66e798958d1e437fdb5da +0 -0
  132. data/test/dummy/tmp/cache/assets/D57/420/sprockets%2F937ea7429c536578ec7b688dee0cdf41 +0 -0
  133. data/test/dummy/tmp/cache/assets/D69/6F0/sprockets%2F94fff7f55bc4c300b25f3f9361ac1a52 +0 -0
  134. data/test/dummy/tmp/cache/assets/D86/D00/sprockets%2Fa4f32b4234d0d1bba272cd75e0d48e1d +0 -0
  135. data/test/dummy/tmp/cache/assets/D8B/B60/sprockets%2Faa32227c440a378ccd21218eefeb80bf +0 -0
  136. data/test/dummy/tmp/cache/assets/D9F/0B0/sprockets%2Faf0d2e69be3a6b56a76c20bf14d9e468 +0 -0
  137. data/test/dummy/tmp/cache/assets/DA8/910/sprockets%2Fab5775c4a837bd4d97ac394d473cda9b +0 -0
  138. data/test/dummy/tmp/cache/assets/DC0/100/sprockets%2F7a5d4f0d352bceed0dce0449c82251bd +0 -0
  139. data/test/dummy/tmp/cache/assets/DD2/490/sprockets%2Fa452ee92a092bc2feabc572cce49896d +0 -0
  140. data/test/dummy/tmp/cache/assets/DE1/320/sprockets%2F9f44ecdec8ceeef70871e15d88a448b1 +0 -0
  141. data/test/dummy/tmp/cache/assets/DF8/5D0/sprockets%2Fb815ed34d61cfed96222daa3bfd1d84d +0 -0
  142. data/test/dummy/tmp/cache/assets/E16/C70/sprockets%2F21ad93418ff75ceac86c7a85dfffe8f6 +0 -0
  143. data/test/dummy/tmp/cache/assets/E35/4F0/sprockets%2F96b1cdf8db6a1c8eb8abcce05958ae74 +0 -0
  144. data/test/dummy/tmp/cache/assets/E4E/300/sprockets%2Fefaae6e1a19a7c8f3acebdd5a36a6103 +0 -0
  145. data/test/fixtures/myreplicator/exports.yml +11 -0
  146. data/test/functional/myreplicator/exports_controller_test.rb +51 -0
  147. data/test/integration/navigation_test.rb +10 -0
  148. data/test/myreplicator_test.rb +7 -0
  149. data/test/test_helper.rb +15 -0
  150. data/test/unit/helpers/myreplicator/exports_helper_test.rb +6 -0
  151. data/test/unit/myreplicator/export_test.rb +10 -0
  152. data/test/unit/myreplicator/exporter_test.rb +11 -0
  153. data/test/unit/myreplicator/exporter_test.rb~ +10 -0
  154. 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
@@ -0,0 +1,5 @@
1
+ module Myreplicator
2
+ module SqlComands
3
+
4
+ end
5
+ 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
@@ -0,0 +1,4 @@
1
+ module Myreplicator
2
+ class Transporter
3
+ end
4
+ end
@@ -0,0 +1,5 @@
1
+ module Myreplicator
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Myreplicator
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module Myreplicator
2
+ VERSION = "0.0.1"
3
+ end
@@ -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,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :myreplicator do
3
+ # # Task goes here
4
+ # 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