groonga-delta 1.0.2 → 1.0.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: ccfa632a6c046239e0163f15306f5479a64c220aa9e419bd415376bfeff5eae2
4
- data.tar.gz: 3ab279d8f62f66bda8e83fc71c3cb27b5472339e5e373868ccbfde0376759c67
3
+ metadata.gz: 5837cbbd7824ebf557d24be10007166c476dadf2157a96071522307d0ffbd0d7
4
+ data.tar.gz: '086fa03eb3a6c9d1a055ec8ffecf0a8b7e9102dcee0fd1d3326ece684f4e5560'
5
5
  SHA512:
6
- metadata.gz: eaa509b863868cd7fa1944d52603c312dc980707d975db9b52dd8b74131b2eac293cecc71dab48abb0895a22203dc2c59942750186e491209a3f395b481496b0
7
- data.tar.gz: 377115552df709288fdff0f1eff36d76e1a99b14e83409f2ab29f5d263b15e94b756d387f357f50423f2015c67f66badb662100d8dac8fdacac67f53d45af0c4
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
@@ -64,7 +64,7 @@ module GroongaDelta
64
64
  end
65
65
 
66
66
  def log_age
67
- @data["log_age"] || 7
67
+ @data["log_age"] || 100
68
68
  end
69
69
 
70
70
  def log_max_size
@@ -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 file
42
- self["file"]
41
+ def last_file
42
+ self["last_file"] || self["file"] # For backward compatibility
43
43
  end
44
44
 
45
- def position
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
- case ENV["GROONGA_DELTA_IMPORT_MYSQL_SOURCE_BACKEND"]
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
- replication_client.file_name = file
134
- current_event_position = last_table_map_position
135
- replication_client.start_position = current_event_position
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
- file = event.file_name
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
- last_table_map_event = current_event_position
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
- event_name = event.class.name.split("::").last
150
- normalized_type =
151
- event_name.scan(/[A-Z][a-z]+/).
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
- current_event_position = event.next_position
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
- def import_rows_event(type,
178
- database_name,
179
- table_name,
180
- file,
181
- next_position,
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
- target_rows = block.call
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 type
197
- when :write_rows_event,
198
- :update_rows_event
108
+ case event
109
+ when Mysql2Replication::WriteRowsEvent,
110
+ Mysql2Replication::UpdateRowsEvent
199
111
  @writer.write_upserts(groonga_table.name, groonga_records)
200
- when :delete_rows_event
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
- @status.update("file" => file,
208
- "position" => next_position,
209
- "last_table_map_position" => last_table_map_position)
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.file
284
- [@status.file, @status.position, @status.last_table_map_position]
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
- file = nil
287
- position = 0
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
- file = result["File"]
293
- position = result["Position"]
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
- @status.update("file" => file,
308
- "position" => position,
309
- "last_table_map_position" => position)
310
- [file, position, position]
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
@@ -35,9 +35,11 @@ module GroongaDelta
35
35
  def update(data)
36
36
  @data.update(data)
37
37
  FileUtils.mkdir_p(@dir)
38
- File.open(@path, "w") do |output|
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
@@ -14,5 +14,5 @@
14
14
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
15
 
16
16
  module GroongaDelta
17
- VERSION = "1.0.2"
17
+ VERSION = "1.0.3"
18
18
  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.2
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-06-21 00:00:00.000000000 Z
11
+ date: 2022-07-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: groonga-client