flydata 0.5.6 → 0.5.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/flydata-core/lib/flydata-core/agent.rb +8 -0
- data/flydata-core/lib/flydata-core/mysql/binlog_pos.rb +72 -0
- data/flydata.gemspec +7 -4
- data/lib/flydata.rb +1 -0
- data/lib/flydata/agent.rb +17 -0
- data/lib/flydata/api/data_entry.rb +11 -5
- data/lib/flydata/command/sender.rb +11 -1
- data/lib/flydata/command/sync.rb +303 -7
- data/lib/flydata/errors.rb +5 -0
- data/lib/flydata/fluent-plugins/in_mysql_binlog_flydata.rb +19 -0
- data/lib/flydata/sync_file_manager.rb +62 -16
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 498996a0198e22b19c7417c3b40fe1be38f2826f
|
4
|
+
data.tar.gz: 1e069123fe4fead123563f63f43a4a5f0f663394
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 395895f16250333b5f7f094ff063cfafacc404c4f1628f29f10f3585844852e452f96ac18effc39043238ac770b3afbaeeeabb78095b3e3b6eae2aff44be0409
|
7
|
+
data.tar.gz: f7e2c589cdc767b5d0edc1f134755e379bb6808903ec411532a6d575388b7e2a823c0cef4766d501806f1e05dd13f526fe44a4ac85af893423499240a0367d6a
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.7
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module FlydataCore
|
2
|
+
module Mysql
|
3
|
+
|
4
|
+
class BinlogPos
|
5
|
+
def initialize(binlog_str_or_filename_or_hash, binlog_pos = nil)
|
6
|
+
arg = binlog_str_or_filename_or_hash
|
7
|
+
if binlog_pos
|
8
|
+
@pos = binlog_pos
|
9
|
+
@filename = arg
|
10
|
+
elsif arg.kind_of?(String)
|
11
|
+
@filename, @pos = arg.split("\t")
|
12
|
+
elsif arg.kind_of?(Hash)
|
13
|
+
@pos = arg[:pos]
|
14
|
+
@filename = arg[:filename]
|
15
|
+
@filename ||= arg[:binfile]
|
16
|
+
end
|
17
|
+
if @filename.nil? || @pos.nil?
|
18
|
+
raise "Invalid initialize argument (#{arg}, #{binlog_pos})"
|
19
|
+
end
|
20
|
+
@pos = @pos.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :filename, :pos
|
24
|
+
|
25
|
+
def <=>(other_pos)
|
26
|
+
if other_pos.nil?
|
27
|
+
raise ArgumentError.new("comparison of BinlosPos with nil failed")
|
28
|
+
end
|
29
|
+
|
30
|
+
other_pos = BinlogPos.new(other_pos) unless other_pos.kind_of?(BinlogPos)
|
31
|
+
|
32
|
+
if @filename < other_pos.filename
|
33
|
+
-1
|
34
|
+
elsif @filename == other_pos.filename
|
35
|
+
if @pos < other_pos.pos
|
36
|
+
-1
|
37
|
+
elsif @pos == other_pos.pos
|
38
|
+
0
|
39
|
+
else
|
40
|
+
1
|
41
|
+
end
|
42
|
+
else
|
43
|
+
1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
def <(other_pos)
|
47
|
+
self.<=>(other_pos) < 0
|
48
|
+
end
|
49
|
+
def <=(other_pos)
|
50
|
+
self.<=>(other_pos) <= 0
|
51
|
+
end
|
52
|
+
def ==(other_pos)
|
53
|
+
return false if other_pos.nil?
|
54
|
+
self.<=>(other_pos) == 0
|
55
|
+
end
|
56
|
+
def !=(other_pos)
|
57
|
+
!self.==(other_pos)
|
58
|
+
end
|
59
|
+
def >(other_pos)
|
60
|
+
self.<=>(other_pos) > 0
|
61
|
+
end
|
62
|
+
def >=(other_pos)
|
63
|
+
self.<=>(other_pos) >= 0
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_s
|
67
|
+
"#{@filename}\t#{@pos}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
data/flydata.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: flydata 0.5.
|
5
|
+
# stub: flydata 0.5.7 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "flydata"
|
9
|
-
s.version = "0.5.
|
9
|
+
s.version = "0.5.7"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Koichi Fujikawa", "Masashi Miyazaki", "Matthew Luu", "Mak Inada", "Sriram NS"]
|
14
|
-
s.date = "2015-09-
|
14
|
+
s.date = "2015-09-23"
|
15
15
|
s.description = "FlyData Agent"
|
16
16
|
s.email = "sysadmin@flydata.com"
|
17
17
|
s.executables = ["fdmysqldump", "flydata", "serverinfo"]
|
@@ -37,6 +37,7 @@ Gem::Specification.new do |s|
|
|
37
37
|
"flydata-core/Gemfile.lock",
|
38
38
|
"flydata-core/circle.yml",
|
39
39
|
"flydata-core/lib/flydata-core.rb",
|
40
|
+
"flydata-core/lib/flydata-core/agent.rb",
|
40
41
|
"flydata-core/lib/flydata-core/config/user_maintenance.rb",
|
41
42
|
"flydata-core/lib/flydata-core/core_ext.rb",
|
42
43
|
"flydata-core/lib/flydata-core/core_ext/module.rb",
|
@@ -47,6 +48,7 @@ Gem::Specification.new do |s|
|
|
47
48
|
"flydata-core/lib/flydata-core/fluent-plugins/multi_buffer.rb",
|
48
49
|
"flydata-core/lib/flydata-core/fluent/config_helper.rb",
|
49
50
|
"flydata-core/lib/flydata-core/logger.rb",
|
51
|
+
"flydata-core/lib/flydata-core/mysql/binlog_pos.rb",
|
50
52
|
"flydata-core/lib/flydata-core/mysql/command_generator.rb",
|
51
53
|
"flydata-core/lib/flydata-core/mysql/compatibility_checker.rb",
|
52
54
|
"flydata-core/lib/flydata-core/mysql/config.rb",
|
@@ -92,6 +94,7 @@ Gem::Specification.new do |s|
|
|
92
94
|
"flydata.gemspec",
|
93
95
|
"lib/fly_data_model.rb",
|
94
96
|
"lib/flydata.rb",
|
97
|
+
"lib/flydata/agent.rb",
|
95
98
|
"lib/flydata/api/agent.rb",
|
96
99
|
"lib/flydata/api/base.rb",
|
97
100
|
"lib/flydata/api/data_entry.rb",
|
@@ -228,7 +231,7 @@ Gem::Specification.new do |s|
|
|
228
231
|
]
|
229
232
|
s.homepage = "http://flydata.com/"
|
230
233
|
s.licenses = ["All right reserved."]
|
231
|
-
s.rubygems_version = "2.4.
|
234
|
+
s.rubygems_version = "2.4.3"
|
232
235
|
s.summary = "FlyData Agent"
|
233
236
|
|
234
237
|
if s.respond_to? :specification_version then
|
data/lib/flydata.rb
CHANGED
@@ -29,6 +29,7 @@ module Flydata
|
|
29
29
|
FLYDATA_SERVERINFO = File.join(FLYDATA_GEM_BIN, 'serverinfo')
|
30
30
|
FLYDATA_LOG = File.join(FLYDATA_HOME, 'flydata.log')
|
31
31
|
FLYDATA_CONF = File.join(FLYDATA_HOME, 'flydata.conf')
|
32
|
+
FLYDATA_LOCK = File.join(FLYDATA_HOME, 'flydata.lock')
|
32
33
|
FLYDATA_HELPER_HOME = File.join(FLYDATA_HOME, 'helper')
|
33
34
|
|
34
35
|
VERSION_PATH = File.join(FLYDATA_GEM_HOME, 'VERSION')
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'flydata-core/agent'
|
2
|
+
|
3
|
+
module Flydata
|
4
|
+
|
5
|
+
class Agent
|
6
|
+
def initialize(flydata_home)
|
7
|
+
@flydata_home = flydata_home
|
8
|
+
end
|
9
|
+
|
10
|
+
def delete_buffer_files
|
11
|
+
files = Dir.glob("#{@flydata_home}/#{FlydataCore::Agent::BUFFER_PATH}*")
|
12
|
+
File.delete(*files)
|
13
|
+
files
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -10,13 +10,19 @@ module Flydata
|
|
10
10
|
super
|
11
11
|
end
|
12
12
|
|
13
|
-
def buffer_stat(data_entry_id,
|
14
|
-
tables =
|
15
|
-
@client.get("/#{@model_name.pluralize}/#{data_entry_id}/buffer_stat/#{
|
13
|
+
def buffer_stat(data_entry_id, options = {})
|
14
|
+
tables = options[:tables] ? options[:tables].join(',') : ''
|
15
|
+
@client.get("/#{@model_name.pluralize}/#{data_entry_id}/buffer_stat/#{options[:mode]}?tables=#{tables}")
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
|
18
|
+
def table_status(data_entry_id, options = {})
|
19
|
+
tables = options[:tables] ? options[:tables].join(',') : ''
|
20
|
+
@client.post("/#{@model_name.pluralize}/#{data_entry_id}/table_status", nil, {tables: tables})
|
21
|
+
end
|
22
|
+
|
23
|
+
def cleanup_sync(data_entry_id, tables, options = {})
|
24
|
+
params = options.merge({tables: tables.join(',')})
|
25
|
+
@client.post("/#{@model_name.pluralize}/#{data_entry_id}/cleanup_sync", nil, params)
|
20
26
|
end
|
21
27
|
|
22
28
|
# Update validity of tables
|
@@ -22,6 +22,11 @@ module Flydata
|
|
22
22
|
return
|
23
23
|
end
|
24
24
|
|
25
|
+
if agent_locked?
|
26
|
+
$log.error "Previous process was terminated abnormally. To start, remove the lock file after checking data integrity."
|
27
|
+
raise "Agent was not shutdown properly. Agent is stopped temporarily in order to avoid sync consistency issues. Please contact support@flydata.com to solve the issue."
|
28
|
+
end
|
29
|
+
|
25
30
|
# Ends orphan_proceses if there is any
|
26
31
|
orphan_processes.each do |pid|
|
27
32
|
Process.kill(:TERM, pid)
|
@@ -72,8 +77,9 @@ EOF
|
|
72
77
|
raise 'Something has gone wrong..'
|
73
78
|
end
|
74
79
|
def flush_client_buffer(options = {})
|
80
|
+
force_flush = options.has_key?(:force) ? options[:force] : true
|
75
81
|
unless process_exist?
|
76
|
-
return true if client_buffer_empty?
|
82
|
+
return true if !force_flush || client_buffer_empty?
|
77
83
|
log_info_stdout("Process doesn't exist. But, the client buffer is not empty!!") unless options[:quiet]
|
78
84
|
raw_start(options)
|
79
85
|
end
|
@@ -124,6 +130,10 @@ EOF
|
|
124
130
|
raise 'Something has gone wrong...'
|
125
131
|
end
|
126
132
|
end
|
133
|
+
# call the method only when no legit process is running.
|
134
|
+
def agent_locked?
|
135
|
+
File.exists?(FLYDATA_LOCK)
|
136
|
+
end
|
127
137
|
|
128
138
|
private
|
129
139
|
# Return a list of fluentd parent processes run by the same user for the
|
data/lib/flydata/command/sync.rb
CHANGED
@@ -2,6 +2,7 @@ require 'msgpack'
|
|
2
2
|
require 'mysql2'
|
3
3
|
require 'rest_client'
|
4
4
|
require 'sys/filesystem'
|
5
|
+
require 'flydata/agent'
|
5
6
|
require 'flydata/command/base'
|
6
7
|
require 'flydata/command/conf'
|
7
8
|
require 'flydata/command/sender'
|
@@ -13,6 +14,7 @@ require 'flydata/parser/mysql/dump_parser'
|
|
13
14
|
require 'flydata/preference/data_entry_preference'
|
14
15
|
require 'flydata/sync_file_manager'
|
15
16
|
require 'flydata-core/table_def'
|
17
|
+
require 'flydata-core/mysql/binlog_pos'
|
16
18
|
require 'flydata/mysql/table_ddl'
|
17
19
|
require 'flydata-core/mysql/command_generator'
|
18
20
|
#require 'ruby-prof'
|
@@ -159,7 +161,7 @@ EOS
|
|
159
161
|
message = ''
|
160
162
|
if sync_resumed && !tables.empty?
|
161
163
|
log_info_stdout <<EOS
|
162
|
-
Initial sync is in progress. In this case, you can only reset the initial sync.To reset specific table(s), please resume and complete the initial sync by running the 'flydata start' command first.
|
164
|
+
Initial sync is in progress. In this case, you can only reset the initial sync. To reset specific table(s), please resume and complete the initial sync by running the 'flydata start' command first.
|
163
165
|
If you'd like to reset the initial sync in progress, run the 'flydata reset' command with no arguments.
|
164
166
|
EOS
|
165
167
|
return
|
@@ -308,6 +310,205 @@ EOS
|
|
308
310
|
end
|
309
311
|
run_exclusive :fix_binlogpos
|
310
312
|
|
313
|
+
def repair
|
314
|
+
de = data_entry
|
315
|
+
set_current_tables
|
316
|
+
# Stop agent. Check sync and make sure the state is :STUCK_AT_UPLOAD
|
317
|
+
# Get table status for the tables.
|
318
|
+
status, pos_mismatch_tables, gap_tables, table_status_hash = _check(stop_agent:true)
|
319
|
+
if status.include? :STUCK_AT_PROCESS
|
320
|
+
e = AgentError.new("Data is stuck while processing")
|
321
|
+
e.description = <<EOS
|
322
|
+
Data appears to be stuck while processing. Contact FlyData Support (support@flydata.com) to solve the issue.
|
323
|
+
EOS
|
324
|
+
raise e
|
325
|
+
end
|
326
|
+
|
327
|
+
if status.include? :OK
|
328
|
+
log_info_stdout
|
329
|
+
log_info_stdout "Sync is in good condition. Nothing to repair."
|
330
|
+
return
|
331
|
+
end
|
332
|
+
|
333
|
+
tables = []
|
334
|
+
gt = gap_tables.collect{|bt| bt[:table] } if gap_tables
|
335
|
+
pt = pos_mismatch_tables.collect{|bt| bt[:table] } if pos_mismatch_tables
|
336
|
+
tables = gt | pt # position mismatch can be due to query queue items discarded by the copy handler so it also needs to be in the target table list.
|
337
|
+
|
338
|
+
log_info_stdout <<EOS
|
339
|
+
|
340
|
+
The following issues have been found and will be repaired.
|
341
|
+
|
342
|
+
EOS
|
343
|
+
log_info_stdout <<EOS unless tables.empty?
|
344
|
+
- Sync is broken due to missing data for the following tables
|
345
|
+
#{tables.join("\n ")}
|
346
|
+
|
347
|
+
EOS
|
348
|
+
|
349
|
+
log_info_stdout <<EOS if status.include? :ABNORMAL_SHUTDOWN
|
350
|
+
- Agent process was not shut down correctly. Files may be corrupt.
|
351
|
+
|
352
|
+
EOS
|
353
|
+
|
354
|
+
return unless ask_yes_no("Proceed?")
|
355
|
+
|
356
|
+
oldest_binlog = get_oldest_available_binlog
|
357
|
+
unrepairable_tables = []
|
358
|
+
# Determine the master binlog positions
|
359
|
+
sent_binlog_pos = nil
|
360
|
+
@full_tables.each do |table|
|
361
|
+
binlog_str = table_status_hash[table]["src_pos"]
|
362
|
+
unless binlog_str
|
363
|
+
raise "Table `#{table}` has no 'src_pos' in its table status"
|
364
|
+
end
|
365
|
+
binlog_pos = FlydataCore::Mysql::BinlogPos.new(binlog_str)
|
366
|
+
if tables.empty?
|
367
|
+
if sent_binlog_pos.nil? || sent_binlog_pos < binlog_pos
|
368
|
+
sent_binlog_pos = binlog_pos
|
369
|
+
end
|
370
|
+
else
|
371
|
+
if tables.include?(table)
|
372
|
+
if sent_binlog_pos.nil? || sent_binlog_pos > binlog_pos
|
373
|
+
if oldest_binlog && binlog_pos < oldest_binlog
|
374
|
+
unrepairable_tables << table
|
375
|
+
else
|
376
|
+
sent_binlog_pos = binlog_pos
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
if oldest_binlog && sent_binlog_pos < oldest_binlog
|
383
|
+
e = AgentError.new("Repair failed due to expired binlog")
|
384
|
+
e.description = <<EOS
|
385
|
+
Repair failed because the starting binlog position `#{sent_binlog_pos} no longer exists. Run full initial sync instead.
|
386
|
+
EOS
|
387
|
+
raise e
|
388
|
+
end
|
389
|
+
master_binlog_pos = FlydataCore::Mysql::BinlogPos.new(sent_binlog_pos.filename, 4)
|
390
|
+
|
391
|
+
# Delete agent buffer
|
392
|
+
log_info_stdout "Deleting data in the agent buffer..."
|
393
|
+
files = Flydata::Agent.new(FLYDATA_HOME).delete_buffer_files
|
394
|
+
unless files.empty?
|
395
|
+
$log.debug "Deleted buffer files\n " + files.join("\n ")
|
396
|
+
end
|
397
|
+
|
398
|
+
# Delete query queue items for the tables
|
399
|
+
log_info_stdout "Deleting data stuck in the server buffer..."
|
400
|
+
cleanup_sync_server(de, tables, queue_only: true) unless tables.empty?
|
401
|
+
|
402
|
+
# Save the positions (binlog and seq)
|
403
|
+
log_info_stdout "Fixing table positions..."
|
404
|
+
@full_tables.each do |table|
|
405
|
+
binlog_str = table_status_hash[table]["src_pos"]
|
406
|
+
unless binlog_str
|
407
|
+
raise "Table `#{table}` has no 'src_pos' in its table status"
|
408
|
+
end
|
409
|
+
binlog_pos = FlydataCore::Mysql::BinlogPos.new(binlog_str)
|
410
|
+
pos = table_status_hash[table]["seq"]
|
411
|
+
old_binlog_pos, old_pos = save_table_positions(table, binlog_pos, pos)
|
412
|
+
if pos.to_i != old_pos.to_i && !tables.include?(table)
|
413
|
+
$log.debug "Fixed broken table position. table:#{table} pos:#{old_pos} -> #{pos}"
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
log_info_stdout "Fixing the master position files..."
|
418
|
+
save_master_binlog_positions(master_binlog_pos, sent_binlog_pos)
|
419
|
+
|
420
|
+
# Remove the lock file if exists.
|
421
|
+
File.delete(FLYDATA_LOCK) if File.exists?(FLYDATA_LOCK)
|
422
|
+
|
423
|
+
log_info_stdout "Repair is done. Start Agent with `flydata start` command."
|
424
|
+
end
|
425
|
+
run_exclusive :repair
|
426
|
+
|
427
|
+
|
428
|
+
def check(options = {})
|
429
|
+
status, pos_mismatch_tables, gap_tables = _check(options)
|
430
|
+
|
431
|
+
if status.include? :OK
|
432
|
+
message = "\nNo errors are found. Sync is clean.\n"
|
433
|
+
else
|
434
|
+
message = "\nFollowing errors are found.\n"
|
435
|
+
|
436
|
+
if status.include? :STUCK_AT_PROCESS
|
437
|
+
message += " - Data is stuck while processing\n"
|
438
|
+
end
|
439
|
+
if status.include? :STUCK_AT_UPLOAD
|
440
|
+
message += " - Data is stuck while uploading\n"
|
441
|
+
end
|
442
|
+
if status.include? :ABNORMAL_SHUTDOWN
|
443
|
+
message += " - Agent was not shut down correctly\n"
|
444
|
+
end
|
445
|
+
if gap_tables
|
446
|
+
message += " - Sync data is missing for the following table(s)\n"
|
447
|
+
gap_tables.each do |bt|
|
448
|
+
message += " table:#{bt[:table]}\n"
|
449
|
+
end
|
450
|
+
message += "\n"
|
451
|
+
end
|
452
|
+
if pos_mismatch_tables
|
453
|
+
message += " - Incorrect table position(s)\n"
|
454
|
+
pos_mismatch_tables.each do |bt|
|
455
|
+
message += " table:#{bt[:table]}, agent position:#{bt[:agent_seq] ? bt[:agent_seq] : '(missing)'}, server position:#{bt[:server_seq]}\n"
|
456
|
+
end
|
457
|
+
message += "\n"
|
458
|
+
end
|
459
|
+
end
|
460
|
+
log_info_stdout message
|
461
|
+
end
|
462
|
+
run_exclusive :check
|
463
|
+
|
464
|
+
def _check(options = {})
|
465
|
+
options[:stop_agent] ||= false
|
466
|
+
|
467
|
+
set_current_tables
|
468
|
+
sender = Flydata::Command::Sender.new
|
469
|
+
start_process = !options[:stop_agent] && sender.process_exist?
|
470
|
+
pos_mismatch_tables = nil
|
471
|
+
gap_tables = nil
|
472
|
+
data_stuck_at = nil
|
473
|
+
abnormal_shutdown = false
|
474
|
+
begin
|
475
|
+
begin
|
476
|
+
flush_buffer_and_stop(@full_tables, force: false, timeout: 55)
|
477
|
+
rescue ServerDataProcessingTimeout => e
|
478
|
+
data_stuck_at = e.state
|
479
|
+
end
|
480
|
+
|
481
|
+
# Agent is stopped but locked. There was an abnormal shutdown.
|
482
|
+
abnormal_shutdown = sender.agent_locked?
|
483
|
+
table_status_hash = get_table_status(@full_tables)
|
484
|
+
pos_mismatch_tables = check_position_files(table_status_hash)
|
485
|
+
gap_tables = check_gaps(table_status_hash)
|
486
|
+
ensure
|
487
|
+
Flydata::Command::Sender.new.start(quiet: true) if start_process
|
488
|
+
end
|
489
|
+
|
490
|
+
status = []
|
491
|
+
if data_stuck_at == :PROCESS
|
492
|
+
status << :STUCK_AT_PROCESS
|
493
|
+
end
|
494
|
+
if data_stuck_at == :UPLOAD
|
495
|
+
status << :STUCK_AT_UPLOAD
|
496
|
+
end
|
497
|
+
if gap_tables
|
498
|
+
status << :TABLE_GAPS
|
499
|
+
end
|
500
|
+
if pos_mismatch_tables
|
501
|
+
status << :TABLE_POS_MISMATCH
|
502
|
+
end
|
503
|
+
if abnormal_shutdown
|
504
|
+
status << :ABNORMAL_SHUTDOWN
|
505
|
+
end
|
506
|
+
if status.empty?
|
507
|
+
status << :OK
|
508
|
+
end
|
509
|
+
[status, pos_mismatch_tables, gap_tables, table_status_hash]
|
510
|
+
end
|
511
|
+
|
311
512
|
private
|
312
513
|
|
313
514
|
# Initial sync
|
@@ -711,7 +912,7 @@ EOM
|
|
711
912
|
end
|
712
913
|
prev_message = status['message']
|
713
914
|
if timeout > 0 && Time.now - start_time > timeout
|
714
|
-
raise ServerDataProcessingTimeout.new
|
915
|
+
raise ServerDataProcessingTimeout.new(nil, state: state)
|
715
916
|
end
|
716
917
|
print_progress(status)
|
717
918
|
sleep 10
|
@@ -824,12 +1025,12 @@ EOM
|
|
824
1025
|
end
|
825
1026
|
end
|
826
1027
|
|
827
|
-
def cleanup_sync_server(de, tables = [])
|
1028
|
+
def cleanup_sync_server(de, tables = [], options = {})
|
828
1029
|
print("Cleaning the queued items on the FlyData Servers.")
|
829
1030
|
log_info("Cleaning the queued items on the FlyData Servers.")
|
830
1031
|
worker = Thread.new do
|
831
1032
|
begin
|
832
|
-
flydata.data_entry.cleanup_sync(de['id'], tables)
|
1033
|
+
flydata.data_entry.cleanup_sync(de['id'], tables, options)
|
833
1034
|
rescue RestClient::RequestTimeout, RestClient::GatewayTimeout
|
834
1035
|
# server is taking time to cleanup. Try again
|
835
1036
|
retry
|
@@ -932,13 +1133,14 @@ Thank you for using FlyData!
|
|
932
1133
|
|
933
1134
|
def flush_buffer_and_stop(tables = [], options = {})
|
934
1135
|
sender = Flydata::Command::Sender.new
|
935
|
-
sender.flush_client_buffer
|
1136
|
+
sender.flush_client_buffer(options)
|
936
1137
|
sender.stop(quiet: true)
|
937
1138
|
|
938
1139
|
return if options[:skip_flush]
|
939
1140
|
|
940
|
-
|
941
|
-
|
1141
|
+
timeout = options.has_key?(:timeout) ? options[:timeout]
|
1142
|
+
: SERVER_DATA_PROCESSING_TIMEOUT
|
1143
|
+
wait_for_server_data_processing( timeout: timeout, tables: tables)
|
942
1144
|
end
|
943
1145
|
|
944
1146
|
# Utility methods
|
@@ -1050,6 +1252,100 @@ Thank you for using FlyData!
|
|
1050
1252
|
end
|
1051
1253
|
raise "These tables are not registered tables: #{inval_table.join(", ")}" unless inval_table.empty?
|
1052
1254
|
end
|
1255
|
+
|
1256
|
+
def check_position_files(table_status_hash)
|
1257
|
+
de = data_entry
|
1258
|
+
sync_fm = create_sync_file_manager(de)
|
1259
|
+
pos_mismatch_tables = []
|
1260
|
+
table_status_hash.keys.collect do |table|
|
1261
|
+
table_status = table_status_hash[table]
|
1262
|
+
|
1263
|
+
server_sequence = table_status["seq"]
|
1264
|
+
agent_sequence = sync_fm.get_table_position(table)
|
1265
|
+
agent_sequence = agent_sequence.to_i if agent_sequence
|
1266
|
+
if server_sequence != agent_sequence
|
1267
|
+
pos_mismatch_tables << {table: table, agent_seq: agent_sequence, server_seq: server_sequence}
|
1268
|
+
end
|
1269
|
+
end
|
1270
|
+
pos_mismatch_tables.empty? ? nil : pos_mismatch_tables
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
def check_gaps(table_status_hash)
|
1274
|
+
gap_tables = table_status_hash.values.select {|table_status|
|
1275
|
+
table_status["num_items"] > 0 && table_status["next_item"] == false
|
1276
|
+
}.collect{|table_status| {table: table_status["table_name"]}}
|
1277
|
+
gap_tables.empty? ? nil : gap_tables
|
1278
|
+
end
|
1279
|
+
|
1280
|
+
def save_table_positions(table, binlog_pos, pos)
|
1281
|
+
de = data_entry
|
1282
|
+
sync_fm = create_sync_file_manager(de)
|
1283
|
+
s = sync_fm.get_table_binlog_pos(table)
|
1284
|
+
old_binlog_pos = s ? FlydataCore::Mysql::BinlogPos.new(s) : nil
|
1285
|
+
old_pos = sync_fm.get_table_position(table)
|
1286
|
+
if pos.to_i != old_pos.to_i
|
1287
|
+
sync_fm.save_table_position(table, pos)
|
1288
|
+
$log.debug "table pos updated. table:#{table} pos:#{old_pos} -> #{pos}"
|
1289
|
+
end
|
1290
|
+
if binlog_pos != old_binlog_pos
|
1291
|
+
sync_fm.save_table_binlog_pos(table, binlog_pos.to_s, destination: :positions)
|
1292
|
+
$log.debug "table binlog updated. table:#{table} binlog:`#{old_binlog_pos}` -> `#{binlog_pos}`"
|
1293
|
+
end
|
1294
|
+
[old_binlog_pos, old_pos]
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
def save_master_binlog_positions(master_binlog_pos, sent_binlog_pos)
|
1298
|
+
de = data_entry
|
1299
|
+
sync_fm = create_sync_file_manager(de)
|
1300
|
+
s = sync_fm.load_binlog
|
1301
|
+
old_master_binlog_pos = s ? FlydataCore::Mysql::BinlogPos.new(s) : nil
|
1302
|
+
s = sync_fm.load_sent_binlog
|
1303
|
+
old_sent_binlog_pos = s ? FlydataCore::Mysql::BinlogPos.new(s) : nil
|
1304
|
+
if master_binlog_pos != old_master_binlog_pos
|
1305
|
+
sync_fm.save_binlog(master_binlog_pos.to_s)
|
1306
|
+
$log.debug "master binlog positions updated. `#{old_master_binlog_pos}` -> `#{master_binlog_pos}`"
|
1307
|
+
end
|
1308
|
+
if sent_binlog_pos != old_sent_binlog_pos
|
1309
|
+
sync_fm.save_sent_binlog(sent_binlog_pos.to_s)
|
1310
|
+
$log.debug "sent binlog positions updated. `#{old_sent_binlog_pos}` -> `#{sent_binlog_pos}`"
|
1311
|
+
end
|
1312
|
+
[old_master_binlog_pos, old_sent_binlog_pos]
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
def get_table_status(tables)
|
1316
|
+
de = data_entry
|
1317
|
+
sync_fm = create_sync_file_manager(de)
|
1318
|
+
result = flydata.data_entry.table_status(de['id'], mode: env_mode, tables: tables)
|
1319
|
+
result = result["table_status"]
|
1320
|
+
|
1321
|
+
table_status_hash = result.inject({}){|h, ts| h[ts["table_name"]] = ts; h}
|
1322
|
+
missing_tables = tables - table_status_hash.keys
|
1323
|
+
unless missing_tables.empty?
|
1324
|
+
raise "table status is not available for these table(s): #{missing_tables.join(",")}"
|
1325
|
+
end
|
1326
|
+
|
1327
|
+
populate_initial_binlog_positions(table_status_hash, sync_fm)
|
1328
|
+
table_status_hash
|
1329
|
+
end
|
1330
|
+
|
1331
|
+
# table_status has no binlog position for sequence "0". Populate the info
|
1332
|
+
# from 'table.binlog.pos.init' file.
|
1333
|
+
def populate_initial_binlog_positions(table_status_hash, sync_fm)
|
1334
|
+
table_status_hash.keys.each do |table|
|
1335
|
+
if table_status_hash[table]["src_pos"] == "-"
|
1336
|
+
init_binlog_pos = sync_fm.get_table_binlog_pos_init(table)
|
1337
|
+
unless init_binlog_pos
|
1338
|
+
raise "File `#{table}.binlog.pos.init` is missing"
|
1339
|
+
end
|
1340
|
+
table_status_hash[table]["src_pos"] = init_binlog_pos
|
1341
|
+
end
|
1342
|
+
end
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
# TODO implement
|
1346
|
+
def get_oldest_available_binlog
|
1347
|
+
nil
|
1348
|
+
end
|
1053
1349
|
end
|
1054
1350
|
end
|
1055
1351
|
end
|
data/lib/flydata/errors.rb
CHANGED
@@ -135,9 +135,17 @@ class MysqlBinlogFlydataInput < MysqlBinlogInput
|
|
135
135
|
)
|
136
136
|
@record_dispatcher = Mysql::FlydataBinlogRecordDispatcher.new(@context)
|
137
137
|
@idle_event_detector = IdleEventDetector.new(@initial_idle_interval, @continuous_idle_interval, @check_interval, @idle_timeout)
|
138
|
+
@lock_file = Flydata::FLYDATA_LOCK
|
138
139
|
end
|
139
140
|
|
140
141
|
def start
|
142
|
+
if File.exists?(@lock_file)
|
143
|
+
$log.error "Previous process was terminated abnormally. To start, remove the lock file after checking data integrity."
|
144
|
+
@abort = true
|
145
|
+
Process.kill(:TERM, Process.ppid)
|
146
|
+
return
|
147
|
+
end
|
148
|
+
|
141
149
|
super
|
142
150
|
@idle_event_detector.start do |reason, timestamp|
|
143
151
|
case reason
|
@@ -155,6 +163,8 @@ class MysqlBinlogFlydataInput < MysqlBinlogInput
|
|
155
163
|
|
156
164
|
positions_path = @context.sync_fm.table_positions_dir_path
|
157
165
|
Dir.mkdir positions_path unless File.exists? positions_path
|
166
|
+
|
167
|
+
File.open(@lock_file, "w") {|f| f.write(Process.pid)}
|
158
168
|
rescue Binlog::Error
|
159
169
|
if (/basic_string::_M_replace_aux/ === $!.to_s)
|
160
170
|
# TODO Fix the root cause in mysql-replication-listener
|
@@ -172,6 +182,8 @@ EOS
|
|
172
182
|
end
|
173
183
|
|
174
184
|
def run
|
185
|
+
return if instance_variable_defined? :@abort
|
186
|
+
|
175
187
|
@context.table_meta.update
|
176
188
|
Flydata::Mysql::TableDdl.migrate_tables(@context.tables, @db_opts,
|
177
189
|
@context.sync_fm, @position_file,
|
@@ -252,6 +264,13 @@ EOS
|
|
252
264
|
end
|
253
265
|
|
254
266
|
def shutdown
|
267
|
+
return if instance_variable_defined? :@abort
|
268
|
+
|
269
|
+
if File.exists?(@lock_file) &&
|
270
|
+
Process.pid == File.open(@lock_file, "r") {|f| f.read}.to_i
|
271
|
+
File.delete(@lock_file)
|
272
|
+
end
|
273
|
+
|
255
274
|
@idle_event_detector.stop
|
256
275
|
if @thread and @thread.alive?
|
257
276
|
$log.info "Requesting stop Kodama"
|
@@ -6,9 +6,12 @@ module Flydata
|
|
6
6
|
DUMP_DIR = ENV['FLYDATA_DUMP'] || File.join(FLYDATA_HOME, 'dump')
|
7
7
|
BACKUP_DIR = ENV['FLYDATA_BACKUP'] || File.join(FLYDATA_HOME, 'backup')
|
8
8
|
TABLE_POSITIONS_DIR = ENV['FLYDATA_TABLE_POSITIONS'] || File.join(FLYDATA_HOME, 'positions')
|
9
|
+
SYNC_TABLE_POSITIONS = 0
|
10
|
+
|
9
11
|
def initialize(data_entry)
|
10
12
|
@data_entry = data_entry
|
11
13
|
@table_position_files = {} # File objects keyed by table name
|
14
|
+
@sync_table_positions_count = SYNC_TABLE_POSITIONS
|
12
15
|
end
|
13
16
|
|
14
17
|
def close
|
@@ -112,6 +115,13 @@ module Flydata
|
|
112
115
|
end
|
113
116
|
end
|
114
117
|
|
118
|
+
def load_sent_binlog(file_path = sent_binlog_path)
|
119
|
+
return nil unless File.exists?(file_path)
|
120
|
+
f, pos = IO.read(file_path).strip.split("\t")
|
121
|
+
return nil if f.nil? || f.empty? || pos.nil?
|
122
|
+
{ binfile: f, pos: pos.to_i }
|
123
|
+
end
|
124
|
+
|
115
125
|
def sent_binlog_path(master_binlog_path = binlog_path)
|
116
126
|
unless master_binlog_path && master_binlog_path.end_with?('binlog.pos')
|
117
127
|
raise ArgumentError.new("Invalid binlog path. binlog path needs to end with 'binlog.pos'")
|
@@ -174,7 +184,7 @@ module Flydata
|
|
174
184
|
file = File.join(table_positions_dir_path, table_name + ".pos")
|
175
185
|
retry_count = 0
|
176
186
|
begin
|
177
|
-
@table_position_files[table_name] ||= File.open(file,
|
187
|
+
@table_position_files[table_name] ||= (f = File.open(file, File::RDWR); f.sync = true; f)
|
178
188
|
rescue Errno::ENOENT
|
179
189
|
raise if retry_count > 0 # Already retried. Must be a differentfile causing the error
|
180
190
|
# File not exist. Create one with initial value of '0'
|
@@ -184,22 +194,43 @@ module Flydata
|
|
184
194
|
end
|
185
195
|
f = @table_position_files[table_name]
|
186
196
|
seq = f.read
|
197
|
+
prev_seq_len = seq.size
|
198
|
+
f.rewind
|
187
199
|
seq = seq.to_i + 1
|
188
200
|
seq = FlydataCore::QueryJob::SYNC_FIRST_SEQ if seq == 1
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
# must be rewound.
|
195
|
-
f.rewind
|
201
|
+
new_seq_len = seq.to_s.size
|
202
|
+
|
203
|
+
seq_to_write = seq.to_s
|
204
|
+
if new_seq_len < prev_seq_len
|
205
|
+
seq_to_write += " " * (prev_seq_len - new_seq_len)
|
196
206
|
end
|
197
|
-
|
198
|
-
|
199
|
-
f.
|
207
|
+
# logical transaction starts
|
208
|
+
yield(seq)
|
209
|
+
f.write(seq_to_write)
|
210
|
+
if @sync_table_positions_count > 1
|
211
|
+
@sync_table_positions_count -= 1
|
212
|
+
elsif @sync_table_positions_count == 1
|
213
|
+
f.fsync
|
214
|
+
@sync_table_positions_count = SYNC_TABLE_POSITIONS
|
215
|
+
end
|
216
|
+
# logical transaction ends
|
217
|
+
f.truncate(new_seq_len) if new_seq_len < prev_seq_len
|
200
218
|
f.rewind
|
201
219
|
end
|
202
220
|
|
221
|
+
def save_table_position(table_name, seq)
|
222
|
+
file = File.join(table_positions_dir_path, table_name + ".pos")
|
223
|
+
|
224
|
+
File.open(file, "w") {|f| f.write(seq)}
|
225
|
+
end
|
226
|
+
|
227
|
+
def get_table_position(table_name)
|
228
|
+
file = File.join(table_positions_dir_path, table_name + ".pos")
|
229
|
+
return nil unless File.exists?(file)
|
230
|
+
|
231
|
+
File.open(file) {|f| f.read}
|
232
|
+
end
|
233
|
+
|
203
234
|
def sync_info_file
|
204
235
|
File.join(dump_dir, "sync.info")
|
205
236
|
end
|
@@ -217,8 +248,8 @@ module Flydata
|
|
217
248
|
tables: items[1].split(" ") }
|
218
249
|
end
|
219
250
|
|
220
|
-
def
|
221
|
-
file = File.join(table_positions_dir_path, table_name + ".binlog.pos")
|
251
|
+
def get_table_binlog_pos_init(table_name)
|
252
|
+
file = File.join(table_positions_dir_path, table_name + ".binlog.pos.init")
|
222
253
|
return nil unless File.exists?(file)
|
223
254
|
File.open(file, 'r').readline
|
224
255
|
end
|
@@ -263,15 +294,28 @@ module Flydata
|
|
263
294
|
end
|
264
295
|
end
|
265
296
|
|
266
|
-
def save_table_binlog_pos(tables, binlog_pos)
|
297
|
+
def save_table_binlog_pos(tables, binlog_pos, options = {})
|
298
|
+
dest_dir = case options[:destination]
|
299
|
+
when :positions; table_positions_dir_path
|
300
|
+
when :dump; dump_dir
|
301
|
+
else dump_dir
|
302
|
+
end
|
303
|
+
|
304
|
+
tables = [ tables ] unless tables.kind_of?(Array)
|
267
305
|
tables.each do |table_name|
|
268
|
-
file = File.join(
|
306
|
+
file = File.join(dest_dir, table_name + ".binlog.pos")
|
269
307
|
File.open(file, "w") do |f|
|
270
308
|
f.write(binlog_content(binlog_pos))
|
271
309
|
end
|
272
310
|
end
|
273
311
|
end
|
274
312
|
|
313
|
+
def get_table_binlog_pos(table_name)
|
314
|
+
file = File.join(table_positions_dir_path, table_name + ".binlog.pos")
|
315
|
+
return nil unless File.exists?(file)
|
316
|
+
File.open(file, 'r').readline
|
317
|
+
end
|
318
|
+
|
275
319
|
def install_table_binlog_files(tables)
|
276
320
|
FileUtils.mkdir_p(table_positions_dir_path) unless Dir.exists?(table_positions_dir_path)
|
277
321
|
tables.each do |table_name|
|
@@ -331,7 +375,9 @@ module Flydata
|
|
331
375
|
end
|
332
376
|
|
333
377
|
def binlog_content(binlog_pos)
|
334
|
-
|
378
|
+
binlog_pos.kind_of?(Hash) ?
|
379
|
+
[binlog_pos[:binfile], binlog_pos[:pos]].join("\t")
|
380
|
+
: binlog_pos
|
335
381
|
end
|
336
382
|
|
337
383
|
def load_mysql_table_marshal_dump
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flydata
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Koichi Fujikawa
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2015-09-
|
15
|
+
date: 2015-09-23 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: rest-client
|
@@ -474,6 +474,7 @@ files:
|
|
474
474
|
- flydata-core/Gemfile.lock
|
475
475
|
- flydata-core/circle.yml
|
476
476
|
- flydata-core/lib/flydata-core.rb
|
477
|
+
- flydata-core/lib/flydata-core/agent.rb
|
477
478
|
- flydata-core/lib/flydata-core/config/user_maintenance.rb
|
478
479
|
- flydata-core/lib/flydata-core/core_ext.rb
|
479
480
|
- flydata-core/lib/flydata-core/core_ext/module.rb
|
@@ -484,6 +485,7 @@ files:
|
|
484
485
|
- flydata-core/lib/flydata-core/fluent-plugins/multi_buffer.rb
|
485
486
|
- flydata-core/lib/flydata-core/fluent/config_helper.rb
|
486
487
|
- flydata-core/lib/flydata-core/logger.rb
|
488
|
+
- flydata-core/lib/flydata-core/mysql/binlog_pos.rb
|
487
489
|
- flydata-core/lib/flydata-core/mysql/command_generator.rb
|
488
490
|
- flydata-core/lib/flydata-core/mysql/compatibility_checker.rb
|
489
491
|
- flydata-core/lib/flydata-core/mysql/config.rb
|
@@ -529,6 +531,7 @@ files:
|
|
529
531
|
- flydata.gemspec
|
530
532
|
- lib/fly_data_model.rb
|
531
533
|
- lib/flydata.rb
|
534
|
+
- lib/flydata/agent.rb
|
532
535
|
- lib/flydata/api/agent.rb
|
533
536
|
- lib/flydata/api/base.rb
|
534
537
|
- lib/flydata/api/data_entry.rb
|
@@ -682,7 +685,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
682
685
|
version: '0'
|
683
686
|
requirements: []
|
684
687
|
rubyforge_project:
|
685
|
-
rubygems_version: 2.4.
|
688
|
+
rubygems_version: 2.4.3
|
686
689
|
signing_key:
|
687
690
|
specification_version: 4
|
688
691
|
summary: FlyData Agent
|