gamma_replication 0.1.1 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6fcf79da2a605e4984ba168b86117cc087f4da9414fc3075949ec3de0e4150e0
|
4
|
+
data.tar.gz: 369e57afcf359c2f148a0a3c985641ccf5beaa3706aade4c48e210827d5c04ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c669c00890ad60fdfe242ad8e472f4f7b63154e4755d22bd798223c47536384f94f51dc8ae6bb2cd8aef83c386f5ac3f72906dcb4b4cccb709beb5282df9a2fa
|
7
|
+
data.tar.gz: f7ff79e999e05d3dabe4e5bd73fc9c0d8bf1939d0faef44391ba212eb5c8516809e65c50c5ee972e044cb6b7d68805c172ad18924f01b53644e2146a68a761ce
|
data/exe/gamma_replication
CHANGED
@@ -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)
|
@@ -48,7 +100,8 @@ module GammaReplication
|
|
48
100
|
|
49
101
|
process_data_by_type(data)
|
50
102
|
rescue StandardError => e
|
51
|
-
|
103
|
+
error_message = e.message.to_s.split("\n").first
|
104
|
+
logger.error("Error: #{error_message.gsub(/\s+/, " ")}")
|
52
105
|
end
|
53
106
|
|
54
107
|
def should_process_data?(data)
|
@@ -64,15 +117,24 @@ module GammaReplication
|
|
64
117
|
table_setting = @table_settings[data["table"]]
|
65
118
|
case data["type"]
|
66
119
|
when "insert"
|
120
|
+
return unless data["data"].present?
|
121
|
+
|
122
|
+
update_stats(data["table"], :insert)
|
67
123
|
process_insert(table_setting, data)
|
68
124
|
when "update"
|
125
|
+
return unless data["data"].present?
|
126
|
+
|
127
|
+
update_stats(data["table"], :update)
|
69
128
|
process_update(table_setting, data)
|
70
129
|
when "delete"
|
130
|
+
return unless data["old"].present?
|
131
|
+
|
132
|
+
update_stats(data["table"], :delete)
|
71
133
|
process_delete(table_setting, data)
|
72
134
|
end
|
73
135
|
rescue StandardError => e
|
74
|
-
|
75
|
-
#
|
136
|
+
error_message = e.message.to_s.split("\n").first
|
137
|
+
logger.error("Error processing #{data["type"]} for table #{data["table"]}: #{error_message.gsub(/\s+/, " ")}")
|
76
138
|
end
|
77
139
|
|
78
140
|
def process_insert(table_setting, data)
|
@@ -100,8 +162,9 @@ module GammaReplication
|
|
100
162
|
|
101
163
|
def process_delete(table_setting, data)
|
102
164
|
old_record = data["old"]
|
103
|
-
|
165
|
+
return unless old_record.present? && old_record[table_setting.primary_key].present?
|
104
166
|
|
167
|
+
where_clause = build_where_clause(old_record, nil, table_setting.primary_key)
|
105
168
|
query = "DELETE FROM #{table_setting.table_name} WHERE #{where_clause}"
|
106
169
|
execute_query(query)
|
107
170
|
end
|
@@ -120,7 +183,8 @@ module GammaReplication
|
|
120
183
|
elsif new_record.present? && new_record[primary_key].present?
|
121
184
|
"`#{primary_key}` = #{format_value(new_record[primary_key])}"
|
122
185
|
else
|
123
|
-
|
186
|
+
logger.error("Primary key not found. old_record: #{old_record.inspect}, new_record: #{new_record.inspect}")
|
187
|
+
raise "Primary key '#{primary_key}' not found in record"
|
124
188
|
end
|
125
189
|
end
|
126
190
|
|
@@ -133,7 +197,34 @@ module GammaReplication
|
|
133
197
|
when Time
|
134
198
|
"'#{value.strftime("%Y-%m-%d %H:%M:%S")}'"
|
135
199
|
else
|
136
|
-
|
200
|
+
if json_column?(value)
|
201
|
+
sanitized_value = sanitize_json(value)
|
202
|
+
"'#{@out_client.client.escape(sanitized_value)}'"
|
203
|
+
else
|
204
|
+
"'#{@out_client.client.escape(value.to_s)}'"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def json_column?(value)
|
210
|
+
value.is_a?(String) && (value.start_with?("{") || value.start_with?("["))
|
211
|
+
end
|
212
|
+
|
213
|
+
def sanitize_json(value)
|
214
|
+
JSON.parse(value)
|
215
|
+
value
|
216
|
+
rescue JSON::ParserError => e
|
217
|
+
sanitized = value.gsub(/([{,]\s*)(\w+)(\s*:)/) do
|
218
|
+
"#{::Regexp.last_match(1)}\"#{::Regexp.last_match(2)}\"#{::Regexp.last_match(3)}"
|
219
|
+
end
|
220
|
+
sanitized = sanitized.gsub(/:\s*([^",\s\d\[\]{}-].*?)(,|\}|$)/) do
|
221
|
+
": \"#{::Regexp.last_match(1)}\"#{::Regexp.last_match(2)}"
|
222
|
+
end
|
223
|
+
begin
|
224
|
+
JSON.parse(sanitized)
|
225
|
+
sanitized
|
226
|
+
rescue JSON::ParserError
|
227
|
+
value.to_json
|
137
228
|
end
|
138
229
|
end
|
139
230
|
|
@@ -19,8 +19,9 @@ module GammaReplication
|
|
19
19
|
io.each do |line|
|
20
20
|
data = JSON.parse(line.strip)
|
21
21
|
block.call(data) if block_given?
|
22
|
-
rescue JSON::ParserError
|
23
|
-
|
22
|
+
rescue JSON::ParserError
|
23
|
+
# Ignore Maxwell's non-JSON output (startup messages, etc)
|
24
|
+
next
|
24
25
|
end
|
25
26
|
end
|
26
27
|
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.
|
4
|
+
version: 0.1.3
|
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-
|
11
|
+
date: 2025-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|