gamma_replication 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/exe/gamma_replication +4 -0
- data/lib/gamma_replication/command/base_replication.rb +94 -5
- data/lib/gamma_replication/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39c63ef1a8906600d9df6b25658cbd4a1b8674be75cb6b5d3d870419f9e5f75e
|
4
|
+
data.tar.gz: ad13bc1e0d79976532f368cd555e59cb89300c920743f35d145b7a928a588ae9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6a5aa663b77e7354ff500e7fcb3fe936b103edf466442e8e44b9e31297da5c6dd4eec0ba12472f195490f710ab6d3dbd3a351311e2676d1edd104b73b396752f
|
7
|
+
data.tar.gz: bc597b21d63e0135ffa154116db12acf4578678db85ab9ef090df30e7247d078e1907460f377c4baa0111317ae9dde8e0bbc5ab9e57cbcda68a9288074e5ab6f
|
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)
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
|
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.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-
|
11
|
+
date: 2025-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|