gamma_replication 0.1.1 → 0.1.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b68929290d56de956de9597cfbdb9c6c7e2574f26489b5b55fc0223aa7d3c6ad
4
- data.tar.gz: 550aa21f4800a9da12f9d4f7b5bb4565a999321623d700cb74ae360bac461ae5
3
+ metadata.gz: 39c63ef1a8906600d9df6b25658cbd4a1b8674be75cb6b5d3d870419f9e5f75e
4
+ data.tar.gz: ad13bc1e0d79976532f368cd555e59cb89300c920743f35d145b7a928a588ae9
5
5
  SHA512:
6
- metadata.gz: 73720919700ce8c4158bee20a58f375fe5a160ba1e44aa0f4348ed3f7ba0ba60d75c3224563579c65c5616dabd4f764b997c936dee029c12a2bade898f410028
7
- data.tar.gz: 10cf80dc382210270080adf30148a7556938bb164633bce1336484f3fdf22be571622ab711e2289aefc7486b8d1c3ea54e12932a117af77540eda7ba73045023
6
+ metadata.gz: 6a5aa663b77e7354ff500e7fcb3fe936b103edf466442e8e44b9e31297da5c6dd4eec0ba12472f195490f710ab6d3dbd3a351311e2676d1edd104b73b396752f
7
+ data.tar.gz: bc597b21d63e0135ffa154116db12acf4578678db85ab9ef090df30e7247d078e1907460f377c4baa0111317ae9dde8e0bbc5ab9e57cbcda68a9288074e5ab6f
@@ -12,6 +12,8 @@ class GammaReplicationCLI < Thor
12
12
  option :data, aliases: "-d", desc: "Table Sync Settings yaml", required: true
13
13
  option :hook_dir, aliases: "-h", desc: "Hook script directory", default: "."
14
14
  option :maxwell_config, aliases: "-m", desc: "Maxwell configuration file path", required: true
15
+ option :enable_stats, type: :boolean, desc: "Enable statistics output (default: true)"
16
+ option :stats_interval, type: :numeric, desc: "Statistics output interval in seconds (default: 10800)"
15
17
  def start
16
18
  GammaReplication::Command::Start.new(options).execute
17
19
  end
@@ -21,6 +23,8 @@ class GammaReplicationCLI < Thor
21
23
  option :data, aliases: "-d", desc: "Table Sync Settings yaml", required: true
22
24
  option :hook_dir, aliases: "-h", desc: "Hook script directory", default: "."
23
25
  option :maxwell_config, aliases: "-m", desc: "Maxwell configuration file path", required: true
26
+ option :enable_stats, type: :boolean, desc: "Enable statistics output (default: true)"
27
+ option :stats_interval, type: :numeric, desc: "Statistics output interval in seconds (default: 10800)"
24
28
  def dryrun
25
29
  GammaReplication::Command::Dryrun.new(options).execute
26
30
  end
@@ -8,6 +8,9 @@ module GammaReplication
8
8
  setup_database(opts)
9
9
  setup_parser(opts)
10
10
  setup_maxwell(opts)
11
+ initialize_stats
12
+ @enable_stats = opts[:enable_stats].nil? ? true : opts[:enable_stats]
13
+ @stats_interval = (opts[:stats_interval] || 3 * 60 * 60).to_i # Default: 3 hours in seconds
11
14
  end
12
15
 
13
16
  def execute
@@ -18,6 +21,9 @@ module GammaReplication
18
21
  hash[table.table_name] = table
19
22
  end
20
23
  before_start if respond_to?(:before_start)
24
+
25
+ setup_stats_reporter if @enable_stats
26
+
21
27
  @maxwell_client.start do |data|
22
28
  process_maxwell_data(data)
23
29
  end
@@ -25,6 +31,52 @@ module GammaReplication
25
31
 
26
32
  private
27
33
 
34
+ def initialize_stats
35
+ @stats = {
36
+ total: { insert: 0, update: 0, delete: 0 },
37
+ by_table: {}
38
+ }
39
+ @stats_mutex = Mutex.new
40
+ end
41
+
42
+ def setup_stats_reporter
43
+ @stats_thread = Thread.new do
44
+ loop do
45
+ sleep @stats_interval
46
+ output_stats
47
+ reset_stats
48
+ end
49
+ end
50
+ end
51
+
52
+ def update_stats(table_name, operation)
53
+ return unless @enable_stats
54
+
55
+ @stats_mutex.synchronize do
56
+ @stats[:total][operation] += 1
57
+ @stats[:by_table][table_name] ||= { insert: 0, update: 0, delete: 0 }
58
+ @stats[:by_table][table_name][operation] += 1
59
+ end
60
+ end
61
+
62
+ def reset_stats
63
+ @stats_mutex.synchronize do
64
+ initialize_stats
65
+ end
66
+ end
67
+
68
+ def output_stats
69
+ @stats_mutex.synchronize do
70
+ total_stats = "Total[ins:#{@stats[:total][:insert]} | upd:#{@stats[:total][:update]} | del:#{@stats[:total][:delete]}]"
71
+
72
+ table_stats = @stats[:by_table].map do |table, counts|
73
+ "#{table}[ins:#{counts[:insert]} | upd:#{counts[:update]} | del:#{counts[:delete]}]"
74
+ end.join("\n")
75
+
76
+ logger.info("STATS | #{total_stats}\n#{table_stats}")
77
+ end
78
+ end
79
+
28
80
  def setup_database(opts)
29
81
  @database_settings = GammaReplication::DatabaseSettings.new(opts[:settings])
30
82
  @in_client = GammaReplication::DatabaseConnector::MysqlConnector.new(@database_settings.in_database)
@@ -64,15 +116,23 @@ module GammaReplication
64
116
  table_setting = @table_settings[data["table"]]
65
117
  case data["type"]
66
118
  when "insert"
119
+ return unless data["data"].present?
120
+
121
+ update_stats(data["table"], :insert)
67
122
  process_insert(table_setting, data)
68
123
  when "update"
124
+ return unless data["data"].present?
125
+
126
+ update_stats(data["table"], :update)
69
127
  process_update(table_setting, data)
70
128
  when "delete"
129
+ return unless data["old"].present?
130
+
131
+ update_stats(data["table"], :delete)
71
132
  process_delete(table_setting, data)
72
133
  end
73
134
  rescue StandardError => e
74
- logger.error(e)
75
- # Nothing
135
+ logger.error("Error processing data: #{data["table"]} - #{e.message}")
76
136
  end
77
137
 
78
138
  def process_insert(table_setting, data)
@@ -100,8 +160,9 @@ module GammaReplication
100
160
 
101
161
  def process_delete(table_setting, data)
102
162
  old_record = data["old"]
103
- where_clause = build_where_clause(old_record, nil, table_setting.primary_key)
163
+ return unless old_record.present? && old_record[table_setting.primary_key].present?
104
164
 
165
+ where_clause = build_where_clause(old_record, nil, table_setting.primary_key)
105
166
  query = "DELETE FROM #{table_setting.table_name} WHERE #{where_clause}"
106
167
  execute_query(query)
107
168
  end
@@ -120,7 +181,8 @@ module GammaReplication
120
181
  elsif new_record.present? && new_record[primary_key].present?
121
182
  "`#{primary_key}` = #{format_value(new_record[primary_key])}"
122
183
  else
123
- raise "Primary key not found in record. old_record: #{old_record.inspect}, new_record: #{new_record.inspect}"
184
+ logger.error("Primary key not found. old_record: #{old_record.inspect}, new_record: #{new_record.inspect}")
185
+ raise "Primary key '#{primary_key}' not found in record"
124
186
  end
125
187
  end
126
188
 
@@ -133,7 +195,34 @@ module GammaReplication
133
195
  when Time
134
196
  "'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
135
197
  else
136
- "'#{@out_client.client.escape(value.to_s)}'"
198
+ if json_column?(value)
199
+ sanitized_value = sanitize_json(value)
200
+ "'#{@out_client.client.escape(sanitized_value)}'"
201
+ else
202
+ "'#{@out_client.client.escape(value.to_s)}'"
203
+ end
204
+ end
205
+ end
206
+
207
+ def json_column?(value)
208
+ value.is_a?(String) && (value.start_with?("{") || value.start_with?("["))
209
+ end
210
+
211
+ def sanitize_json(value)
212
+ JSON.parse(value)
213
+ value
214
+ rescue JSON::ParserError => e
215
+ sanitized = value.gsub(/([{,]\s*)(\w+)(\s*:)/) do
216
+ "#{::Regexp.last_match(1)}\"#{::Regexp.last_match(2)}\"#{::Regexp.last_match(3)}"
217
+ end
218
+ sanitized = sanitized.gsub(/:\s*([^",\s\d\[\]{}-].*?)(,|\}|$)/) do
219
+ ": \"#{::Regexp.last_match(1)}\"#{::Regexp.last_match(2)}"
220
+ end
221
+ begin
222
+ JSON.parse(sanitized)
223
+ sanitized
224
+ rescue JSON::ParserError
225
+ value.to_json
137
226
  end
138
227
  end
139
228
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GammaReplication
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gamma_replication
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shinsuke Nishio
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-01-22 00:00:00.000000000 Z
11
+ date: 2025-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport