rails_redshift_replicator 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 (116) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +34 -0
  5. data/app/assets/javascripts/rails_redshift_replicator/application.js +13 -0
  6. data/app/assets/stylesheets/rails_redshift_replicator/application.css +15 -0
  7. data/app/controllers/rails_redshift_replicator/application_controller.rb +5 -0
  8. data/app/helpers/rails_redshift_replicator/application_helper.rb +4 -0
  9. data/app/models/rails_redshift_replicator/replication.rb +98 -0
  10. data/app/views/layouts/rails_redshift_replicator/application.html.erb +14 -0
  11. data/config/locales/rails_redshift_replicator.en.yml +20 -0
  12. data/config/routes.rb +2 -0
  13. data/db/migrate/20160503214955_create_rails_redshift_replicator_replications.rb +24 -0
  14. data/db/migrate/20160509193335_create_table_rails_redshift_replicator_deleted_ids.rb +8 -0
  15. data/lib/generators/rails_redshift_replicator/install_generator.rb +25 -0
  16. data/lib/generators/templates/rails_redshift_replicator.rb +74 -0
  17. data/lib/rails_redshift_replicator.rb +229 -0
  18. data/lib/rails_redshift_replicator/adapters/generic.rb +40 -0
  19. data/lib/rails_redshift_replicator/adapters/mysql2.rb +22 -0
  20. data/lib/rails_redshift_replicator/adapters/postgresql.rb +37 -0
  21. data/lib/rails_redshift_replicator/adapters/sqlite.rb +27 -0
  22. data/lib/rails_redshift_replicator/deleter.rb +67 -0
  23. data/lib/rails_redshift_replicator/engine.rb +14 -0
  24. data/lib/rails_redshift_replicator/exporters/base.rb +215 -0
  25. data/lib/rails_redshift_replicator/exporters/full_replicator.rb +9 -0
  26. data/lib/rails_redshift_replicator/exporters/identity_replicator.rb +9 -0
  27. data/lib/rails_redshift_replicator/exporters/timed_replicator.rb +9 -0
  28. data/lib/rails_redshift_replicator/file_manager.rb +134 -0
  29. data/lib/rails_redshift_replicator/importers/base.rb +158 -0
  30. data/lib/rails_redshift_replicator/importers/full_replicator.rb +17 -0
  31. data/lib/rails_redshift_replicator/importers/identity_replicator.rb +15 -0
  32. data/lib/rails_redshift_replicator/importers/timed_replicator.rb +18 -0
  33. data/lib/rails_redshift_replicator/model/extension.rb +45 -0
  34. data/lib/rails_redshift_replicator/model/hair_trigger_extension.rb +8 -0
  35. data/lib/rails_redshift_replicator/replicable.rb +143 -0
  36. data/lib/rails_redshift_replicator/rlogger.rb +12 -0
  37. data/lib/rails_redshift_replicator/tools/analyze.rb +18 -0
  38. data/lib/rails_redshift_replicator/tools/vacuum.rb +77 -0
  39. data/lib/rails_redshift_replicator/version.rb +3 -0
  40. data/lib/tasks/rails_redshift_replicator_tasks.rake +4 -0
  41. data/spec/dummy/README.rdoc +28 -0
  42. data/spec/dummy/Rakefile +6 -0
  43. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  44. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  45. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  46. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  47. data/spec/dummy/app/models/post.rb +4 -0
  48. data/spec/dummy/app/models/tag.rb +4 -0
  49. data/spec/dummy/app/models/user.rb +5 -0
  50. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  51. data/spec/dummy/bin/bundle +3 -0
  52. data/spec/dummy/bin/rails +4 -0
  53. data/spec/dummy/bin/rake +4 -0
  54. data/spec/dummy/bin/setup +29 -0
  55. data/spec/dummy/config.ru +4 -0
  56. data/spec/dummy/config/application.rb +26 -0
  57. data/spec/dummy/config/boot.rb +5 -0
  58. data/spec/dummy/config/database.yml +37 -0
  59. data/spec/dummy/config/environment.rb +5 -0
  60. data/spec/dummy/config/environments/development.rb +41 -0
  61. data/spec/dummy/config/environments/production.rb +79 -0
  62. data/spec/dummy/config/environments/test.rb +42 -0
  63. data/spec/dummy/config/initializers/assets.rb +11 -0
  64. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  65. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  66. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  67. data/spec/dummy/config/initializers/inflections.rb +16 -0
  68. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  69. data/spec/dummy/config/initializers/rails_redshift_replicator.rb +59 -0
  70. data/spec/dummy/config/initializers/session_store.rb +3 -0
  71. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  72. data/spec/dummy/config/locales/en.yml +23 -0
  73. data/spec/dummy/config/locales/rails_redshift_replicator.en.yml +19 -0
  74. data/spec/dummy/config/routes.rb +4 -0
  75. data/spec/dummy/config/secrets.yml +22 -0
  76. data/spec/dummy/db/development.sqlite3 +0 -0
  77. data/spec/dummy/db/migrate/20160504120421_create_test_tables.rb +40 -0
  78. data/spec/dummy/db/migrate/20160509225445_create_triggers_posts_delete_or_tags_delete_or_users_delete.rb +33 -0
  79. data/spec/dummy/db/migrate/20160511000937_create_rails_redshift_replicator_replications.rails_redshift_replicator.rb +25 -0
  80. data/spec/dummy/db/migrate/20160511000938_create_table_rails_redshift_replicator_deleted_ids.rails_redshift_replicator.rb +9 -0
  81. data/spec/dummy/db/schema.rb +99 -0
  82. data/spec/dummy/db/test.sqlite3 +0 -0
  83. data/spec/dummy/log/development.log +1623 -0
  84. data/spec/dummy/log/test.log +95379 -0
  85. data/spec/dummy/public/404.html +67 -0
  86. data/spec/dummy/public/422.html +67 -0
  87. data/spec/dummy/public/500.html +66 -0
  88. data/spec/dummy/public/favicon.ico +0 -0
  89. data/spec/dummy/rails_redshift_replicator_development +0 -0
  90. data/spec/factories/rails_redshift_replicator_replications.rb +31 -0
  91. data/spec/integration/rails_redshift_replicator_spec.rb +148 -0
  92. data/spec/integration/setup_spec.rb +149 -0
  93. data/spec/lib/rails_redshift_replicator/deleter_spec.rb +90 -0
  94. data/spec/lib/rails_redshift_replicator/exporters/base_spec.rb +326 -0
  95. data/spec/lib/rails_redshift_replicator/exporters/full_replicator_spec.rb +33 -0
  96. data/spec/lib/rails_redshift_replicator/exporters/identity_replicator_spec.rb +40 -0
  97. data/spec/lib/rails_redshift_replicator/exporters/timed_replicator_spec.rb +43 -0
  98. data/spec/lib/rails_redshift_replicator/file_manager_spec.rb +90 -0
  99. data/spec/lib/rails_redshift_replicator/importers/base_spec.rb +102 -0
  100. data/spec/lib/rails_redshift_replicator/importers/full_replicator_spec.rb +27 -0
  101. data/spec/lib/rails_redshift_replicator/importers/identity_replicator_spec.rb +26 -0
  102. data/spec/lib/rails_redshift_replicator/importers/timed_replicator_spec.rb +26 -0
  103. data/spec/lib/rails_redshift_replicator/model/extension_spec.rb +36 -0
  104. data/spec/lib/rails_redshift_replicator/replicable_spec.rb +230 -0
  105. data/spec/lib/rails_redshift_replicator/rlogger_spec.rb +22 -0
  106. data/spec/lib/rails_redshift_replicator/tools/analyze_spec.rb +15 -0
  107. data/spec/lib/rails_redshift_replicator/tools/vacuum_spec.rb +65 -0
  108. data/spec/lib/rails_redshift_replicator_spec.rb +110 -0
  109. data/spec/models/rails_redshift_replicator/replication_spec.rb +104 -0
  110. data/spec/spec_helper.rb +36 -0
  111. data/spec/support/csv/invalid_user.csv +12 -0
  112. data/spec/support/csv/valid_post.csv +2 -0
  113. data/spec/support/csv/valid_tags_users.csv +1 -0
  114. data/spec/support/csv/valid_user.csv +2 -0
  115. data/spec/support/rails_redshift_replicator_helpers.rb +95 -0
  116. metadata +430 -0
@@ -0,0 +1,40 @@
1
+ module RailsRedshiftReplicator
2
+ module Adapters
3
+ class Generic
4
+ def initialize(ar_client)
5
+ @ar_client = ar_client
6
+ end
7
+
8
+ # Returns the AR connection
9
+ def connection
10
+ @connection ||= @ar_client
11
+ end
12
+
13
+ # Executes query using AR Adapter
14
+ # @param sql [String] sql to execute
15
+ def query_command(sql)
16
+ connection.query sql
17
+ end
18
+
19
+ # @return [String] id or timestamp
20
+ def last_record_query_command(sql)
21
+ connection.exec_query(sql).first['_last_record']
22
+ end
23
+
24
+ # Writes query results to a file
25
+ # @param file_path [String] path to output
26
+ # @param query_result [#each] Resultset from the query_command
27
+ # @return [Integer] number of records
28
+ def write(file_path, query_result)
29
+ line_number = 0
30
+ CSV.open(file_path, "w") do |csv|
31
+ query_result.each do |row|
32
+ csv << row.map{ |field| field.is_a?(String) ? field.gsub("\n", " ") : field }
33
+ line_number+=1
34
+ end
35
+ end
36
+ line_number
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ module RailsRedshiftReplicator
2
+ module Adapters
3
+ class Mysql2 < Generic
4
+
5
+ # Executes query in stream mode to optimize memory usage, using Mysql2 driver.
6
+ # @param sql [String] sql to execute
7
+ def query_command(sql)
8
+ connection.query(sql, stream: true)
9
+ end
10
+
11
+ # @see RailsRedshiftReplicator::Adapters::Generic#last_record_query_command
12
+ def last_record_query_command(sql)
13
+ connection.query(sql, cast: false).first[0] rescue nil
14
+ end
15
+
16
+ # Returns mysql2 driver so that we may perform query with the stream option
17
+ def connection
18
+ @connection ||= @ar_client.instance_variable_get("@connection")
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ module RailsRedshiftReplicator
2
+ module Adapters
3
+ class PostgreSQL < Generic
4
+
5
+ # Executes query in stream mode to optimize memory usage, using pg driver.
6
+ # @param sql [String] sql to execute
7
+ def query_command(sql)
8
+ connection.send_query(sql)
9
+ connection.set_single_row_mode
10
+ end
11
+
12
+ def connection
13
+ @connection ||= @ar_client.instance_variable_get("@connection")
14
+ end
15
+
16
+ # @see RailsRedshiftReplicator::Adapters::Generic#last_record_query_command
17
+ def last_record_query_command(sql)
18
+ @ar_client.exec_query(sql).first['_last_record']
19
+ end
20
+
21
+ # Writes query results to a file
22
+ # @param file_path [String] path to output
23
+ # @param query_result [#get_result] Resultset from the query_command
24
+ # @return [Integer] number of records
25
+ def write(file_path, query_result)
26
+ line_number = 0
27
+ CSV.open(file_path, "w") do |csv|
28
+ query_result.get_result.stream_each do |row|
29
+ csv << row.map{ |_,field| field.is_a?(String) ? field.gsub("\n", " ") : field }
30
+ line_number+=1
31
+ end
32
+ end
33
+ line_number
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ module RailsRedshiftReplicator
2
+ module Adapters
3
+ class SQLite < Generic
4
+
5
+ # @param conn [ActiveRecord::ConnectionAdapter]
6
+ # @param sql [String] sql to execute
7
+ def query_command(sql)
8
+ connection.exec_query sql
9
+ end
10
+
11
+ # Writes query results to a file
12
+ # @param file_path [String] path to output
13
+ # @param query_result [#each] Resultset from the query_command
14
+ # @return [Integer] number of records
15
+ def write(file_path, query_result)
16
+ line_number = 0
17
+ CSV.open(file_path, "w") do |csv|
18
+ query_result.each do |row|
19
+ csv << row.map{ |_,field| field.is_a?(String) ? field.gsub("\n", " ") : field }
20
+ line_number+=1
21
+ end
22
+ end
23
+ line_number
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,67 @@
1
+ module RailsRedshiftReplicator
2
+ class Deleter
3
+ attr_reader :replicable
4
+ def initialize(replicable)
5
+ @replicable = replicable
6
+ end
7
+ # Deletes ids on source database. This ensures that only the records deleted on target
8
+ # will be discarded on source
9
+ def purge_deleted
10
+ ActiveRecord::Base.connection.execute discard_deleted_statement
11
+ end
12
+
13
+ # Deletes records flagged for deletion on redshift
14
+ # @return [true, false] if deletion succeded
15
+ def delete_on_target
16
+ RailsRedshiftReplicator.connection.exec(delete_on_target_statement).result_status == 1
17
+ end
18
+
19
+ # Deletes records flagged for deletion on target and then delete the queue from source
20
+ def handle_delete_propagation
21
+ if replicable.tracking_deleted && has_deleted_ids?
22
+ RailsRedshiftReplicator.logger.info propagation_message(:propagating_deletes)
23
+ delete_on_target ? purge_deleted : RailsRedshiftReplicator.logger.error(propagation_message(:delete_propagation_error))
24
+ end
25
+ end
26
+
27
+ def propagation_message(key)
28
+ I18n.t(key, table_name: replicable.source_table, count: deleted_ids.count, scope: :rails_redshift_replicator)
29
+ end
30
+
31
+ # Retrives ids of objects enqueued for deletion for the replication source table
32
+ # @example
33
+ # deleted_ids #=> "1,2,3,4,5"
34
+ # @return [String] list of ids enqueued to delete on target
35
+ def deleted_ids
36
+ sql = <<-DR.squish
37
+ SELECT object_id
38
+ FROM rails_redshift_replicator_deleted_ids
39
+ WHERE source_table = '#{replicable.source_table}'
40
+ DR
41
+ ActiveRecord::Base.connection.execute(sql).map{ |r| r['object_id'] }
42
+ end
43
+
44
+ # If has objects to delete on target
45
+ # @return [true, false]
46
+ def has_deleted_ids?
47
+ deleted_ids.present?
48
+ end
49
+
50
+ # Builds the statement to perform a deletion on source
51
+ # @return [String] SQL statement
52
+ def discard_deleted_statement
53
+ sql = <<-DD.squish
54
+ DELETE FROM rails_redshift_replicator_deleted_ids
55
+ WHERE source_table = '#{replicable.source_table}'
56
+ AND object_id IN(#{deleted_ids.join(",")})
57
+ DD
58
+ end
59
+
60
+ def delete_on_target_statement
61
+ sql = <<-DD.squish
62
+ DELETE FROM #{replicable.target_table}
63
+ WHERE id IN(#{deleted_ids.join(",")})
64
+ DD
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,14 @@
1
+ module RailsRedshiftReplicator
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace RailsRedshiftReplicator
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec, :fixture => false
7
+ g.fixture_replacement :factory_girl, :dir => 'spec/factories'
8
+ end
9
+
10
+ initializer "rrr.initialisation" do |app|
11
+ # ActiveRecord::Base.send :include, RailsRedshiftReplicator::Model
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,215 @@
1
+ require 'csv'
2
+ require 'rails_redshift_replicator/adapters/generic'
3
+ require 'rails_redshift_replicator/adapters/mysql2'
4
+ require 'rails_redshift_replicator/adapters/postgresql'
5
+ require 'rails_redshift_replicator/adapters/sqlite'
6
+
7
+ module RailsRedshiftReplicator
8
+ module Exporters
9
+ class Base
10
+ extend Forwardable
11
+ def_delegators :replicable, :replication_type, :source_table, :target_table, :replication_field, :exporter_class
12
+ attr_reader :replicable
13
+ attr_accessor :replication, :file_names, :errors
14
+
15
+ def initialize(replicable, current_replication = nil)
16
+ @replicable = replicable
17
+ @replication = current_replication
18
+ @file_names = nil
19
+ @errors = nil
20
+ check_target_table
21
+ check_index
22
+ end
23
+
24
+ # Exports and uploads selected records from the source_table
25
+ def export_and_upload(options = {})
26
+ RailsRedshiftReplicator::Deleter.new(replicable).handle_delete_propagation
27
+ files = export options
28
+ upload files
29
+ replication
30
+ end
31
+
32
+ # Lists indexes from source table
33
+ # @return [Array<String>] indexes from source table
34
+ def table_indexes
35
+ ActiveRecord::Base.connection.indexes(source_table).map{ |table| table.columns}.flatten | ["id"]
36
+ end
37
+
38
+ # Verifies if the table has the recommended indexes to increase export performance.
39
+ # @return [true, false] if table has recommended indexes
40
+ def has_index?
41
+ replication_field.in? table_indexes
42
+ end
43
+
44
+ # Reports missing indexes
45
+ # @see #has_index?
46
+ def check_index
47
+ if !has_index? && replication_field.present?
48
+ RailsRedshiftReplicator.logger.warn I18n.t(:missing_indexes, replication_field: replication_field, table_name: source_table, scope: :rails_redshift_replicator)
49
+ end
50
+ end
51
+
52
+ # Checks if target table exists on Redshift
53
+ # @return [true, false] if table exists
54
+ def check_target_table
55
+ unless fields_to_sync
56
+ message = I18n.t(:missing_table, table_name: target_table, scope: :rails_redshift_replicator)
57
+ RailsRedshiftReplicator.logger.error(message)
58
+ @errors = message
59
+ end
60
+ end
61
+
62
+ # Returns records do export
63
+ # @param from_record [Integer] initial record
64
+ # When the exporter type is identity, the record is an id or equivalent
65
+ # When the exporter type is timed, the record is the timestamp converted to epoch (date.to_i)
66
+ # @param options [Hash]
67
+ # @option :options [Boolean] :counts_only if should only return record count instead of records
68
+ # @return records [Array<Array>] each entry has its fields returned as an array
69
+ # @note Query cache is disabled to decrease memory usage.
70
+ def records(from_record = nil, option = {})
71
+ @records ||= begin
72
+ ActiveRecord::Base.uncached do
73
+ query_command build_query_sql(from_record, option)
74
+ end
75
+ end
76
+ end
77
+
78
+ # Builds the SQL string based on replicable and exporter parameters
79
+ # @return sql [String] sql string to perform the export query
80
+ def build_query_sql(from_record = nil, option = {})
81
+ sql = ""
82
+ sql += "SELECT #{option[:counts_only] ? "count(1) as records_count" : fields_to_sync.join(",")}"
83
+ sql += " FROM #{source_table} WHERE 1=1"
84
+ sql += " AND #{replication_field} > '#{from_record}'" if from_record
85
+ sql += " AND #{replication_field} <= '#{last_record}' OR #{replication_field} IS NULL" if last_record
86
+ sql
87
+ end
88
+
89
+ # Returns an instance of a export connection adapter based on ActiveRecord::Base.connection
90
+ # These adapters are required to perform query execution and record retrival in taking advantage of each db driver.
91
+ # @return adapter [#query_command, #write, #last_record_query_command]
92
+ def connection_adapter
93
+ @connection_adapter ||= begin
94
+ adapter_class = if ar_client.adapter_name.in? %w(Mysql2 PostgreSQL SQLite)
95
+ "RailsRedshiftReplicator::Adapters::#{ar_client.adapter_name}".constantize
96
+ else
97
+ RailsRedshiftReplicator::Adapters::Generic
98
+ end
99
+ adapter_class.new ar_client
100
+ end
101
+ end
102
+
103
+ # Performs the query to retrive records to export
104
+ # @param sql [String] sql to execute
105
+ def query_command(sql)
106
+ RailsRedshiftReplicator.logger.debug I18n.t(:executing_query, scope: :rails_redshift_replicator, sql: sql, adapter: connection_adapter.class.name)
107
+ connection_adapter.query_command sql
108
+ end
109
+
110
+ # Returns the ActiveRecord connection adapter for the current database
111
+ def ar_client
112
+ @ar_client ||= ActiveRecord::Base.connection
113
+ end
114
+
115
+ # Retuns the value of last_record from the most recent complete replication record.
116
+ # @return [Integer, nil] last_record
117
+ # @note Some replication strategies may not use a replication_field(eg: FullExporter), so #from_record will be nil
118
+ def from_record
119
+ return if replication_field.blank?
120
+ last_replication.try(:last_record)
121
+ end
122
+
123
+ # Exports results to CSV
124
+ # @return [String] file name
125
+ def export(options = {})
126
+ return if errors.present?
127
+ slices = options[:slices] || RailsRedshiftReplicator.redshift_slices.to_i
128
+ format = options[:format] || RailsRedshiftReplicator.preferred_format
129
+ file_name = file_manager.temp_file_name
130
+ initialize_replication(file_name, format, slices)
131
+ export_start = replication.exporting
132
+ counts = file_manager.write_csv file_name, records(from_record)
133
+ unless counts > 0
134
+ RailsRedshiftReplicator.logger.info I18n.t(:no_new_records, table_name: source_table, scope: :rails_redshift_replicator)
135
+ self.replication = nil
136
+ return
137
+ end
138
+ RailsRedshiftReplicator.logger.info I18n.t(:exporting_results, counts: counts, scope: :rails_redshift_replicator)
139
+ file_manager.split_file file_name, counts
140
+ replication.exported! export_duration: (Time.now-export_start).ceil, record_count: counts
141
+ @file_names = Dir.glob "#{file_manager.local_file(file_name)}*"
142
+ end
143
+
144
+ # @param [String] nome do arquivo
145
+ # Uploads file to s3
146
+ # @param [String] file name
147
+ def upload(files = file_names)
148
+ return if errors.present? || files.blank?
149
+ upload_start = replication.uploading!
150
+ replication.gzip? ? file_manager.upload_gzip(files) : file_manager.upload_csv(files)
151
+ replication.uploaded! upload_duration: (Time.now-upload_start).ceil
152
+ end
153
+
154
+ def file_manager
155
+ @file_manager ||= RailsRedshiftReplicator::FileManager.new(self)
156
+ end
157
+
158
+ # Initialize replication record without saving
159
+ # @return [RailsRedshiftReplicator::Replication]
160
+ def initialize_replication(file_name, format, slices)
161
+ attrs = init_replication_attrs(file_name, format, slices)
162
+ @replication = replication.present? ? replication.assign_attributes(attrs) : RailsRedshiftReplicator::Replication.new(attrs)
163
+ end
164
+
165
+ def init_replication_attrs(file_name, format, slices)
166
+ {
167
+ key: file_manager.file_key_in_format(file_name, format),
168
+ last_record: last_record.to_s,
169
+ state: 'exporting',
170
+ replication_type: replication_type,
171
+ source_table: source_table,
172
+ target_table: target_table,
173
+ slices: slices,
174
+ first_record: from_record,
175
+ export_format: format
176
+ }
177
+ end
178
+
179
+ # Returns the last replication from a given table
180
+ # @return [RailsRedshiftReplicator::Replication] last replication from a given table
181
+ def last_replication
182
+ @last_replication ||= RailsRedshiftReplicator::Replication.from_table(source_table).last
183
+ end
184
+
185
+ # Retuns the last record to export using the replication_field criteria.
186
+ # @note last_record is an Integer and is computed based on the replication field.
187
+ # @return [Integer] content of the
188
+ def last_record
189
+ return if replication_field.blank?
190
+ @last_record ||= begin
191
+ sql = "SELECT max(#{replication_field}) as _last_record from #{source_table}"
192
+ connection_adapter.last_record_query_command(sql)
193
+ end
194
+ end
195
+
196
+ # Schema for the export table on redshift
197
+ # @return [Hash] array of fields per table
198
+ def redshift_schema
199
+ @schema ||= begin
200
+ result = ::RailsRedshiftReplicator.connection.exec("select tablename, \"column\", type from pg_table_def where tablename = '#{target_table}'")
201
+ result.to_a.group_by{ |el| el["tablename"] }
202
+ end
203
+ end
204
+
205
+ # Lists fields on redshift table
206
+ # @return [Array<String>] colunas na tabela do Redshift
207
+ def fields_to_sync
208
+ @fields_to_sync ||= begin
209
+ column_defs = redshift_schema[target_table]
210
+ column_defs.blank? ? nil : column_defs.map{ |col_def| col_def['column'] }
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,9 @@
1
+ module RailsRedshiftReplicator
2
+ module Exporters
3
+ class FullReplicator < Base
4
+ def self.replication_field
5
+ nil
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module RailsRedshiftReplicator
2
+ module Exporters
3
+ class IdentityReplicator < Base
4
+ def self.replication_field
5
+ "id"
6
+ end
7
+ end
8
+ end
9
+ end