groonga-delta 1.0.2 → 1.0.3
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 +4 -4
- data/doc/text/news.md +14 -0
- data/lib/groonga-delta/config.rb +1 -1
- data/lib/groonga-delta/import-config.rb +7 -11
- data/lib/groonga-delta/import-status.rb +8 -4
- data/lib/groonga-delta/mysql-source.rb +72 -137
- data/lib/groonga-delta/status.rb +3 -1
- data/lib/groonga-delta/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: 5837cbbd7824ebf557d24be10007166c476dadf2157a96071522307d0ffbd0d7
|
4
|
+
data.tar.gz: '086fa03eb3a6c9d1a055ec8ffecf0a8b7e9102dcee0fd1d3326ece684f4e5560'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3db1aedcdbf3bb39312bfb091a778091111eec880488917a4d21598834e08a703181f383f47d06e2646e873d1ea1ac5d8e42ed46d71328ee8ecb8c837aa080d
|
7
|
+
data.tar.gz: 584ecff5ca3bf44fc9f4baea4ea8bd5f6c60227ac0f05367dd0860c74a563b26f4bcac747c74a7ce1c98a77f77100c2c736817ca6daa4af8138aec1372996f1c
|
data/doc/text/news.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# NEWS
|
2
2
|
|
3
|
+
## 1.0.3 - 2022-07-21
|
4
|
+
|
5
|
+
### Improvements
|
6
|
+
|
7
|
+
* `import`: Dropped support for `mysqlbinlog`.
|
8
|
+
|
9
|
+
* Changed to use atomic write for `status.yaml`.
|
10
|
+
|
11
|
+
* Changed to keep 100 logs from 7 logs by default.
|
12
|
+
|
13
|
+
### Fixes
|
14
|
+
|
15
|
+
* `import`: Fixed a bug that events to be processed can't be detected.
|
16
|
+
|
3
17
|
## 1.0.2 - 2022-06-21
|
4
18
|
|
5
19
|
### Improvements
|
data/lib/groonga-delta/config.rb
CHANGED
@@ -59,10 +59,6 @@ module GroongaDelta
|
|
59
59
|
resolve_path(@data["binlog_dir"] || "binlog")
|
60
60
|
end
|
61
61
|
|
62
|
-
def mysqlbinlog
|
63
|
-
@data["mysqlbinlog"] || "mysqlbinlog"
|
64
|
-
end
|
65
|
-
|
66
62
|
def host
|
67
63
|
@data["host"] || "localhost"
|
68
64
|
end
|
@@ -183,20 +179,20 @@ module GroongaDelta
|
|
183
179
|
case value
|
184
180
|
when String
|
185
181
|
case value
|
186
|
-
when /\A(\d+(?:\.\d+))(?:s|sec|second|seconds)?\z/
|
182
|
+
when /\A(\d+(?:\.\d+)?)(?:s|sec|second|seconds)?\z/
|
187
183
|
Float($1)
|
188
|
-
when /\A(\d+(?:\.\d+))(?:m|min|minute|minutes)\z/
|
184
|
+
when /\A(\d+(?:\.\d+)?)(?:m|min|minute|minutes)\z/
|
189
185
|
Float($1) * 60
|
190
|
-
when /\A(\d+(?:\.\d+))(?:h|hr|hour|hours)\z/
|
186
|
+
when /\A(\d+(?:\.\d+)?)(?:h|hr|hour|hours)\z/
|
191
187
|
Float($1) * 60 * 60
|
192
|
-
when /\A(\d+(?:\.\d+))(?:d|day|days)\z/
|
188
|
+
when /\A(\d+(?:\.\d+)?)(?:d|day|days)\z/
|
193
189
|
Float($1) * 60 * 60 * 24
|
194
|
-
when /\A(\d+(?:\.\d+))(?:w|week|weeks)\z/
|
190
|
+
when /\A(\d+(?:\.\d+)?)(?:w|week|weeks)\z/
|
195
191
|
Float($1) * 60 * 60 * 24 * 7
|
196
|
-
when /\A(\d+(?:\.\d+))(?:month|months)\z/
|
192
|
+
when /\A(\d+(?:\.\d+)?)(?:month|months)\z/
|
197
193
|
# Same as systemd. See systemd.time(7)
|
198
194
|
Float($1) * 60 * 60 * 24 * 30.44
|
199
|
-
when /\A(\d+(?:\.\d+))(?:y|year|years)\z/
|
195
|
+
when /\A(\d+(?:\.\d+)?)(?:y|year|years)\z/
|
200
196
|
# Same as systemd. See systemd.time(7)
|
201
197
|
Float($1) * 60 * 60 * 24 * 365.25
|
202
198
|
else
|
@@ -38,12 +38,16 @@ module GroongaDelta
|
|
38
38
|
@status.update("mysql" => new_data)
|
39
39
|
end
|
40
40
|
|
41
|
-
def
|
42
|
-
self["file"]
|
41
|
+
def last_file
|
42
|
+
self["last_file"] || self["file"] # For backward compatibility
|
43
43
|
end
|
44
44
|
|
45
|
-
def
|
46
|
-
self["position"]
|
45
|
+
def last_position
|
46
|
+
self["last_position"] || self["position"] # For backward compatibility
|
47
|
+
end
|
48
|
+
|
49
|
+
def last_table_map_file
|
50
|
+
self["last_table_map_file"] || self["file"]
|
47
51
|
end
|
48
52
|
|
49
53
|
def last_table_map_position
|
@@ -15,6 +15,7 @@
|
|
15
15
|
|
16
16
|
require "arrow"
|
17
17
|
require "mysql2"
|
18
|
+
require "mysql2-replication"
|
18
19
|
|
19
20
|
require_relative "error"
|
20
21
|
require_relative "local-writer"
|
@@ -32,92 +33,7 @@ module GroongaDelta
|
|
32
33
|
end
|
33
34
|
|
34
35
|
def import
|
35
|
-
|
36
|
-
when "mysqlbinlog"
|
37
|
-
require "mysql_binlog"
|
38
|
-
import_mysqlbinlog
|
39
|
-
when "mysql2-replication"
|
40
|
-
require "mysql2-replication"
|
41
|
-
import_mysql2_replication
|
42
|
-
else
|
43
|
-
begin
|
44
|
-
require "mysql2-replication"
|
45
|
-
rescue LoadError
|
46
|
-
require "mysql_binlog"
|
47
|
-
import_mysqlbinlog
|
48
|
-
else
|
49
|
-
import_mysql2_replication
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
def import_mysqlbinlog
|
56
|
-
file, position, last_table_map_position = read_current_status
|
57
|
-
FileUtils.mkdir_p(@binlog_dir)
|
58
|
-
local_file = File.join(@binlog_dir, file)
|
59
|
-
unless File.exist?(local_file.succ)
|
60
|
-
command_line = [@config.mysqlbinlog].flatten
|
61
|
-
command_line << "--host=#{@config.host}" if @config.host
|
62
|
-
command_line << "--port=#{@config.port}" if @config.port
|
63
|
-
command_line << "--socket=#{@config.socket}" if @config.socket
|
64
|
-
if @config.replication_slave_user
|
65
|
-
command_line << "--user=#{@config.replication_slave_user}"
|
66
|
-
end
|
67
|
-
if @config.replication_slave_password
|
68
|
-
command_line << "--password=#{@config.replication_slave_password}"
|
69
|
-
end
|
70
|
-
command_line << "--read-from-remote-server"
|
71
|
-
command_line << "--raw"
|
72
|
-
command_line << "--result-file=#{@binlog_dir}/"
|
73
|
-
command_line << file
|
74
|
-
spawn_process(*command_line) do |pid, output_read, error_read|
|
75
|
-
end
|
76
|
-
end
|
77
|
-
reader = MysqlBinlog::BinlogFileReader.new(local_file)
|
78
|
-
binlog = MysqlBinlog::Binlog.new(reader)
|
79
|
-
binlog.checksum = @config.checksum
|
80
|
-
binlog.ignore_rotate = true
|
81
|
-
binlog.each_event do |event|
|
82
|
-
next if event[:position] < last_table_map_position
|
83
|
-
case event[:type]
|
84
|
-
when :rotate_event
|
85
|
-
file = event[:event][:name]
|
86
|
-
when :table_map_event
|
87
|
-
last_table_map_position = event[:position]
|
88
|
-
when :write_rows_event_v1,
|
89
|
-
:write_rows_event_v2,
|
90
|
-
:update_rows_event_v1,
|
91
|
-
:update_rows_event_v2,
|
92
|
-
:delete_rows_event_v1,
|
93
|
-
:delete_rows_event_v2
|
94
|
-
next if event[:position] < position
|
95
|
-
normalized_type = event[:type].to_s.gsub(/_v\d\z/, "").to_sym
|
96
|
-
import_rows_event(normalized_type,
|
97
|
-
event[:event][:table][:db],
|
98
|
-
event[:event][:table][:table],
|
99
|
-
file,
|
100
|
-
event[:header][:next_position],
|
101
|
-
last_table_map_position) do
|
102
|
-
case normalized_type
|
103
|
-
when :write_rows_event,
|
104
|
-
:update_rows_event
|
105
|
-
event[:event][:row_image].collect do |row_image|
|
106
|
-
build_row(row_image[:after][:image])
|
107
|
-
end
|
108
|
-
when :delete_rows_event
|
109
|
-
event[:event][:row_image].collect do |row_image|
|
110
|
-
build_row(row_image[:before][:image])
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
position = event[:header][:next_position]
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def import_mysql2_replication
|
120
|
-
file, position, last_table_map_position = read_current_status
|
36
|
+
current_status = read_current_status
|
121
37
|
is_mysql_56_or_later = mysql(@config.select_user,
|
122
38
|
@config.select_password) do |select_client|
|
123
39
|
mysql_version(select_client) >= Gem::Version.new("5.6")
|
@@ -130,83 +46,85 @@ module GroongaDelta
|
|
130
46
|
replication_client = Mysql2Replication::Client.new(client,
|
131
47
|
checksum: "NONE")
|
132
48
|
end
|
133
|
-
|
134
|
-
|
135
|
-
|
49
|
+
current_file = current_status.last_table_map_file
|
50
|
+
replication_client.file_name = current_file
|
51
|
+
current_event_position = current_status.last_table_map_position
|
52
|
+
if current_event_position
|
53
|
+
replication_client.start_position = current_event_position
|
54
|
+
end
|
136
55
|
replication_client.open do
|
137
56
|
replication_client.each do |event|
|
138
57
|
begin
|
139
58
|
@logger.debug do
|
140
59
|
event.inspect
|
141
60
|
end
|
142
|
-
next if current_event_position < position
|
143
61
|
case event
|
144
62
|
when Mysql2Replication::RotateEvent
|
145
|
-
|
63
|
+
current_file = event.file_name
|
64
|
+
current_status.last_file = current_file
|
65
|
+
current_status.last_position = event.position
|
146
66
|
when Mysql2Replication::TableMapEvent
|
147
|
-
|
67
|
+
current_status.last_table_map_file = current_file
|
68
|
+
current_status.last_table_map_position = current_event_position
|
148
69
|
when Mysql2Replication::RowsEvent
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
collect(&:downcase).
|
153
|
-
join("_").
|
154
|
-
to_sym
|
155
|
-
import_rows_event(normalized_type,
|
156
|
-
event.table_map.database,
|
157
|
-
event.table_map.table,
|
158
|
-
file,
|
159
|
-
event.next_position,
|
160
|
-
last_table_map_position) do
|
161
|
-
case normalized_type
|
162
|
-
when :update_rows_event
|
163
|
-
event.updated_rows
|
164
|
-
else
|
165
|
-
event.rows
|
166
|
-
end
|
167
|
-
end
|
70
|
+
next if current_file != current_status.last_file
|
71
|
+
next if current_event_position < current_status.last_position
|
72
|
+
import_rows_event(event, current_status)
|
168
73
|
end
|
169
74
|
ensure
|
170
|
-
|
75
|
+
# next_position may be 0. For example,
|
76
|
+
# Mysql2Replication::FormatDescriptionEvent#next_position
|
77
|
+
# returns 0.
|
78
|
+
next_position = event.next_position
|
79
|
+
current_event_position = next_position unless next_position.zero?
|
171
80
|
end
|
172
81
|
end
|
173
82
|
end
|
174
83
|
end
|
175
84
|
end
|
176
85
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
last_table_map_position,
|
183
|
-
&block)
|
86
|
+
private
|
87
|
+
def import_rows_event(event, current_status)
|
88
|
+
database_name = event.table_map.database
|
89
|
+
table_name = event.table_map.table
|
90
|
+
|
184
91
|
source_table = @mapping[database_name, table_name]
|
185
92
|
return if source_table.nil?
|
186
93
|
|
187
94
|
table = find_table(database_name, table_name)
|
188
95
|
groonga_table = source_table.groonga_table
|
189
|
-
|
96
|
+
case event
|
97
|
+
when Mysql2Replication::UpdateRowsEvent
|
98
|
+
target_rows = event.updated_rows
|
99
|
+
else
|
100
|
+
target_rows = event.rows
|
101
|
+
end
|
190
102
|
groonga_records = target_rows.collect do |row|
|
191
103
|
record = build_record(table, row)
|
192
104
|
groonga_table.generate_record(record)
|
193
105
|
end
|
194
106
|
return if groonga_records.empty?
|
195
107
|
|
196
|
-
case
|
197
|
-
when
|
198
|
-
|
108
|
+
case event
|
109
|
+
when Mysql2Replication::WriteRowsEvent,
|
110
|
+
Mysql2Replication::UpdateRowsEvent
|
199
111
|
@writer.write_upserts(groonga_table.name, groonga_records)
|
200
|
-
when
|
112
|
+
when Mysql2Replication::DeleteRowsEvent
|
201
113
|
groonga_record_keys = groonga_records.collect do |record|
|
202
114
|
record[:_key]
|
203
115
|
end
|
204
116
|
@writer.write_deletes(groonga_table.name,
|
205
117
|
groonga_record_keys)
|
206
118
|
end
|
207
|
-
|
208
|
-
|
209
|
-
|
119
|
+
current_status.last_position = event.next_position
|
120
|
+
new_status = {
|
121
|
+
"last_file" => current_status.last_file,
|
122
|
+
"last_position" => current_status.last_position,
|
123
|
+
"last_table_map_file" => current_status.last_table_map_file,
|
124
|
+
"last_table_map_position" => current_status.last_table_map_position,
|
125
|
+
}
|
126
|
+
@logger.info("Update status: #{new_status.inspect}")
|
127
|
+
@status.update(new_status)
|
210
128
|
end
|
211
129
|
|
212
130
|
def wait_process(command_line, pid, output_read, error_read)
|
@@ -280,17 +198,20 @@ module GroongaDelta
|
|
280
198
|
end
|
281
199
|
|
282
200
|
def read_current_status
|
283
|
-
if @status.
|
284
|
-
|
201
|
+
if @status.last_file
|
202
|
+
CurrentStatus.new(@status.last_file,
|
203
|
+
@status.last_position,
|
204
|
+
@status.last_table_map_file,
|
205
|
+
@status.last_table_map_position)
|
285
206
|
else
|
286
|
-
|
287
|
-
|
207
|
+
last_file = nil
|
208
|
+
last_position = 0
|
288
209
|
mysql(@config.replication_client_user,
|
289
210
|
@config.replication_client_password) do |replication_client|
|
290
211
|
replication_client.query("FLUSH TABLES WITH READ LOCK")
|
291
212
|
result = replication_client.query("SHOW MASTER STATUS").first
|
292
|
-
|
293
|
-
|
213
|
+
last_file = result["File"]
|
214
|
+
last_position = result["Position"]
|
294
215
|
mysql(@config.select_user,
|
295
216
|
@config.select_password) do |select_client|
|
296
217
|
start_transaction = "START TRANSACTION " +
|
@@ -304,10 +225,19 @@ module GroongaDelta
|
|
304
225
|
select_client.query("ROLLBACK")
|
305
226
|
end
|
306
227
|
end
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
228
|
+
current_status = CurrentStatus.new(last_file,
|
229
|
+
last_position,
|
230
|
+
last_file,
|
231
|
+
last_position)
|
232
|
+
new_status = {
|
233
|
+
"last_file" => current_status.last_file,
|
234
|
+
"last_position" => current_status.last_position,
|
235
|
+
"last_table_map_file" => current_status.last_file,
|
236
|
+
"last_table_map_position" => current_status.last_position,
|
237
|
+
}
|
238
|
+
@logger.info("Update status: #{new_status.inspect}")
|
239
|
+
@status.update(new_status)
|
240
|
+
current_status
|
311
241
|
end
|
312
242
|
end
|
313
243
|
|
@@ -405,5 +335,10 @@ module GroongaDelta
|
|
405
335
|
end
|
406
336
|
record
|
407
337
|
end
|
338
|
+
|
339
|
+
CurrentStatus = Struct.new(:last_file,
|
340
|
+
:last_position,
|
341
|
+
:last_table_map_file,
|
342
|
+
:last_table_map_position)
|
408
343
|
end
|
409
344
|
end
|
data/lib/groonga-delta/status.rb
CHANGED
@@ -35,9 +35,11 @@ module GroongaDelta
|
|
35
35
|
def update(data)
|
36
36
|
@data.update(data)
|
37
37
|
FileUtils.mkdir_p(@dir)
|
38
|
-
|
38
|
+
path_writing = "#{@path}.writing"
|
39
|
+
File.open(path_writing, "w") do |output|
|
39
40
|
output.puts(YAML.dump(@data))
|
40
41
|
end
|
42
|
+
FileUtils.mv(path_writing, @path)
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: groonga-delta
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sutou Kouhei
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-07-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: groonga-client
|