flydata 0.6.14 → 0.7.0
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/Gemfile +1 -0
- data/flydata-core/Gemfile.lock +5 -0
- data/flydata-core/lib/flydata-core/errors.rb +4 -2
- data/flydata-core/lib/flydata-core/mysql/binlog_pos.rb +4 -0
- data/flydata-core/lib/flydata-core/postgresql/compatibility_checker.rb +119 -0
- data/flydata-core/lib/flydata-core/postgresql/config.rb +58 -0
- data/flydata-core/lib/flydata-core/postgresql/pg_client.rb +170 -0
- data/flydata-core/lib/flydata-core/postgresql/snapshot.rb +49 -0
- data/flydata-core/lib/flydata-core/postgresql/source_pos.rb +71 -10
- data/flydata-core/lib/flydata-core/table_def/mysql_table_def.rb +1 -1
- data/flydata-core/lib/flydata-core/table_def/postgresql_table_def.rb +76 -17
- data/flydata-core/lib/flydata-core/table_def/redshift_table_def.rb +59 -10
- data/flydata-core/spec/mysql/binlog_pos_spec.rb +10 -2
- data/flydata-core/spec/postgresql/compatibility_checker_spec.rb +148 -0
- data/flydata-core/spec/postgresql/config_spec.rb +85 -0
- data/flydata-core/spec/postgresql/pg_client_spec.rb +195 -0
- data/flydata-core/spec/postgresql/snapshot_spec.rb +55 -0
- data/flydata-core/spec/postgresql/source_pos_spec.rb +70 -8
- data/flydata-core/spec/table_def/postgresql_table_def_spec.rb +80 -19
- data/flydata-core/spec/table_def/redshift_table_def_spec.rb +211 -14
- data/flydata.gemspec +0 -0
- data/lib/flydata.rb +1 -0
- data/lib/flydata/command/sender.rb +10 -7
- data/lib/flydata/command/sync.rb +4 -1
- data/lib/flydata/fluent-plugins/flydata_plugin_ext/base.rb +1 -0
- data/lib/flydata/fluent-plugins/flydata_plugin_ext/fluent_log_ext.rb +73 -0
- data/lib/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync.rb +35 -10
- data/lib/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_diff_based.rb +29 -0
- data/lib/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_query_based.rb +26 -0
- data/lib/flydata/fluent-plugins/flydata_plugin_ext/preference.rb +29 -13
- data/lib/flydata/fluent-plugins/in_mysql_binlog_flydata.rb +10 -18
- data/lib/flydata/fluent-plugins/in_postgresql_query_based_flydata.rb +64 -0
- data/lib/flydata/helpers.rb +1 -3
- data/lib/flydata/plugin_support/context.rb +14 -2
- data/lib/flydata/plugin_support/source_position_file.rb +35 -0
- data/lib/flydata/plugin_support/sync_record_emittable.rb +2 -1
- data/lib/flydata/query_based_sync/client.rb +101 -0
- data/lib/flydata/query_based_sync/record_size_estimator.rb +39 -0
- data/lib/flydata/query_based_sync/resource_requester.rb +70 -0
- data/lib/flydata/query_based_sync/response.rb +122 -0
- data/lib/flydata/query_based_sync/response_handler.rb +30 -0
- data/lib/flydata/source/sync_generate_table_ddl.rb +1 -1
- data/lib/flydata/source_mysql/plugin_support/binlog_record_dispatcher.rb +2 -2
- data/lib/flydata/source_mysql/plugin_support/binlog_record_handler.rb +3 -9
- data/lib/flydata/source_mysql/plugin_support/context.rb +26 -2
- data/lib/flydata/source_mysql/plugin_support/source_position_file.rb +14 -0
- data/lib/flydata/source_mysql/table_ddl.rb +3 -3
- data/lib/flydata/source_mysql/{plugin_support/table_meta.rb → table_meta.rb} +3 -10
- data/lib/flydata/source_postgresql/generate_source_dump.rb +44 -63
- data/lib/flydata/source_postgresql/parse_dump_and_send.rb +2 -0
- data/lib/flydata/source_postgresql/plugin_support/context.rb +13 -0
- data/lib/flydata/source_postgresql/plugin_support/source_position_file.rb +14 -0
- data/lib/flydata/source_postgresql/query_based_sync/client.rb +16 -0
- data/lib/flydata/source_postgresql/query_based_sync/diff_query_generator.rb +135 -0
- data/lib/flydata/source_postgresql/query_based_sync/resource_requester.rb +86 -0
- data/lib/flydata/source_postgresql/query_based_sync/response.rb +12 -0
- data/lib/flydata/source_postgresql/query_based_sync/response_handler.rb +12 -0
- data/lib/flydata/source_postgresql/sync_generate_table_ddl.rb +25 -79
- data/lib/flydata/source_postgresql/table_meta.rb +168 -0
- data/lib/flydata/sync_file_manager.rb +5 -5
- data/lib/flydata/table_meta.rb +19 -0
- data/spec/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_context.rb +85 -0
- data/spec/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_diff_based_shared_examples.rb +36 -0
- data/spec/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_query_based_shared_examples.rb +37 -0
- data/spec/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_shared_examples.rb +67 -0
- data/spec/flydata/fluent-plugins/in_mysql_binlog_flydata_spec.rb +119 -96
- data/spec/flydata/fluent-plugins/in_postgresql_query_based_flydata_spec.rb +82 -0
- data/spec/flydata/fluent-plugins/sync_source_plugin_context.rb +29 -0
- data/spec/flydata/plugin_support/context_spec.rb +37 -3
- data/spec/flydata/query_based_sync/client_spec.rb +79 -0
- data/spec/flydata/query_based_sync/query_based_sync_context.rb +116 -0
- data/spec/flydata/query_based_sync/record_size_estimator_spec.rb +54 -0
- data/spec/flydata/query_based_sync/resource_requester_spec.rb +58 -0
- data/spec/flydata/query_based_sync/response_handler_spec.rb +36 -0
- data/spec/flydata/query_based_sync/response_spec.rb +157 -0
- data/spec/flydata/source_mysql/plugin_support/context_spec.rb +7 -1
- data/spec/flydata/source_mysql/plugin_support/dml_record_handler_spec.rb +2 -15
- data/spec/flydata/source_mysql/plugin_support/drop_database_query_handler_spec.rb +1 -1
- data/spec/flydata/source_mysql/plugin_support/shared_query_handler_context.rb +12 -11
- data/spec/flydata/source_mysql/plugin_support/source_position_file_spec.rb +53 -0
- data/spec/flydata/source_mysql/plugin_support/truncate_query_handler_spec.rb +1 -1
- data/spec/flydata/source_mysql/table_ddl_spec.rb +5 -5
- data/spec/flydata/source_mysql/{plugin_support/table_meta_spec.rb → table_meta_spec.rb} +6 -7
- data/spec/flydata/source_postgresql/generate_source_dump_spec.rb +165 -77
- data/spec/flydata/source_postgresql/query_based_sync/diff_query_generator_spec.rb +213 -0
- data/spec/flydata/source_postgresql/query_based_sync/query_based_sync_postgresql_context.rb +76 -0
- data/spec/flydata/source_postgresql/query_based_sync/resource_requester_spec.rb +70 -0
- data/spec/flydata/source_postgresql/table_meta_spec.rb +77 -0
- metadata +49 -6
- data/lib/flydata/source_mysql/plugin_support/binlog_position_file.rb +0 -23
- data/lib/flydata/source_postgresql/pg_client.rb +0 -43
@@ -18,13 +18,14 @@ module PluginSupport
|
|
18
18
|
# "records" : A record or records for emitting
|
19
19
|
# Each record needs to be Hash
|
20
20
|
# "options"
|
21
|
+
# type: : (required) type (insert, update, delete)
|
21
22
|
# tag : (optional) tag (default: @context.tag)
|
22
23
|
# timestamp : (optional) timestamp (default: current timestamp)
|
23
24
|
# src_pos : (required) source position (used for sync:repair)
|
24
25
|
# table : (optional) table name
|
25
26
|
# increment_table_rev : (optional) set true when incrementing table revision
|
26
27
|
def emit_sync_records(records, options)
|
27
|
-
return if records.nil? # skip
|
28
|
+
return if records.nil? || records.empty? # skip
|
28
29
|
records = [records] unless records.kind_of?(Array)
|
29
30
|
|
30
31
|
# Check options
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'flydata-core/logger'
|
3
|
+
|
4
|
+
module Flydata
|
5
|
+
module QueryBasedSync
|
6
|
+
|
7
|
+
class Client
|
8
|
+
include FlydataCore::Logger
|
9
|
+
|
10
|
+
DEFAULT_FETCH_INTERVAL = 60 # 1 minute
|
11
|
+
DEFAULT_RETRY_INTERVAL = 30 # 1 minute
|
12
|
+
|
13
|
+
# params
|
14
|
+
# fetch_interval
|
15
|
+
# resource_names
|
16
|
+
def initialize(context)
|
17
|
+
@context = context
|
18
|
+
@resource_requester = self.class::RESOURCE_REQUESTER_CLASS.new(context)
|
19
|
+
@response_handler = self.class::RESPONSE_HANDLER_CLASS.new(context)
|
20
|
+
|
21
|
+
context.params.tap do |c|
|
22
|
+
@fetch_interval = c.nil? ? DEFAULT_FETCH_INTERVAL : c[:fetch_interval]
|
23
|
+
@retry_interval = c.nil? ? DEFAULT_RETRY_INTERVAL : c[:retry_interval]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :context
|
28
|
+
attr_reader :resource_requester
|
29
|
+
attr_reader :response_handler
|
30
|
+
|
31
|
+
def start
|
32
|
+
raise "Already started - thread:#{@running_thread}" if @running_thread
|
33
|
+
raise "Already stop requested" if @stop_requested
|
34
|
+
@running_thread = Thread.current
|
35
|
+
run_loop
|
36
|
+
end
|
37
|
+
|
38
|
+
def stop_request
|
39
|
+
@finish = true
|
40
|
+
if @running_thread && @running_thread.alive?
|
41
|
+
@running_thread.run
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def stop_requested?
|
46
|
+
!!@finish
|
47
|
+
end
|
48
|
+
|
49
|
+
def handle_response(response)
|
50
|
+
log_debug("Handling response", table_name: response.table_name, count:response.records.count,
|
51
|
+
new_snapshot: response.new_source_pos, base_snapshot: context.table_meta.current_snapshot)
|
52
|
+
@response_handler.handle(response)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def run_loop
|
58
|
+
begin
|
59
|
+
until stop_requested?
|
60
|
+
reset_log_context
|
61
|
+
run_once
|
62
|
+
break if stop_requested?
|
63
|
+
sleep @fetch_interval
|
64
|
+
end
|
65
|
+
rescue => e
|
66
|
+
#TODO: Introduce retryable error notions
|
67
|
+
log_error_with_backtrace("Unexpected error occured", error: e)
|
68
|
+
sleep @retry_interval
|
69
|
+
retry
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def run_once
|
74
|
+
resource_requester.start do |req| # open connection
|
75
|
+
context.tables.each do |table_name|
|
76
|
+
add_log_context_items(table_name: table_name)
|
77
|
+
return if stop_requested?
|
78
|
+
req.each_response(table_name) do |response|
|
79
|
+
return if stop_requested?
|
80
|
+
handle_response(response)
|
81
|
+
|
82
|
+
unless response.empty?
|
83
|
+
log_info("Emitted records", count: response.record_count, src_pos:response.new_source_pos)
|
84
|
+
end
|
85
|
+
|
86
|
+
return if stop_requested?
|
87
|
+
end
|
88
|
+
|
89
|
+
delete_log_context_item(:table_name)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Set the current snapshot to master(resume) binlog.pos file
|
94
|
+
context.cur_src_pos_file.save(context.table_meta.current_snapshot)
|
95
|
+
|
96
|
+
log_info("Updated source position -", resume_pos:context.table_meta.current_snapshot)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Flydata
|
2
|
+
module QueryBasedSync
|
3
|
+
|
4
|
+
class RecordSizeEstimator
|
5
|
+
# Data size for the static part of sent record
|
6
|
+
# {"row":{},"type":"update","respect_order":true,
|
7
|
+
# "src_pos":"10051:10051:\t10520:10052:\t{\"id\":\"1000000000\"}",
|
8
|
+
# "v":2,"seq":10000000,"table_name":"","table_rev":1}
|
9
|
+
BASE_DATA_SIZE = 162
|
10
|
+
|
11
|
+
def initialize(table_name, num_columns)
|
12
|
+
@base_record_size = calc_base_record_size(table_name, num_columns)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Calculate a record size for emitting
|
16
|
+
def calc_record_size(record)
|
17
|
+
@base_record_size + record.values.inject(0){|r, v| r+=v.to_s.size}
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def calc_base_record_size(table_name, num_columns)
|
23
|
+
col_cnt = num_columns
|
24
|
+
# static part of {"1":"16","2":"cc", ...}
|
25
|
+
col_size_for_static = 6 * col_cnt - 1 + 2
|
26
|
+
col_size_for_key = 0
|
27
|
+
col_cnt_length = col_cnt.to_s.size # get position of number, ex: 9->1, 12->2, 132->3
|
28
|
+
# calc size of each key per position, 1, 10x, 100x,... ex: 123 -> calc size 1-9 and 10-99
|
29
|
+
1.upto(col_cnt_length-1){|i| col_size_for_key += ( i * (10**i - (10**(i-1))) )}
|
30
|
+
# calc size of the key for the hight position
|
31
|
+
col_size_for_key += (col_cnt - 10**(col_cnt_length-1) + 1) * col_cnt_length
|
32
|
+
col_size = col_size_for_static + col_size_for_key
|
33
|
+
BASE_DATA_SIZE + table_name.size + col_size
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'flydata-core/logger'
|
2
|
+
|
3
|
+
module Flydata
|
4
|
+
module QueryBasedSync
|
5
|
+
|
6
|
+
class ResourceRequester
|
7
|
+
include FlydataCore::Logger
|
8
|
+
|
9
|
+
def initialize(context)
|
10
|
+
@context = context
|
11
|
+
@resource_client = create_resource_client(context)
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :context
|
15
|
+
|
16
|
+
# Start fetching resources by keeping a same connection
|
17
|
+
# Caller needs to call methods using the connection with block
|
18
|
+
#
|
19
|
+
# ex:
|
20
|
+
# requester.start do |req|
|
21
|
+
# req.fetch_resources(table_name) do |response|
|
22
|
+
# handle(response)
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
def start
|
26
|
+
context.table_meta.reload(@resource_client)
|
27
|
+
yield self
|
28
|
+
ensure
|
29
|
+
@resource_client.close
|
30
|
+
end
|
31
|
+
|
32
|
+
# Fetch resources for a table
|
33
|
+
# It may call the callback multiple times in the following cases
|
34
|
+
# - The resource size is bigger than the max size per request
|
35
|
+
# - Resume to fetch resources if resume info exists in per-table position file
|
36
|
+
def each_response(table_name, interval = 1)
|
37
|
+
latest_src_pos = context.source_pos_class.new(context.table_meta.current_snapshot)
|
38
|
+
|
39
|
+
loop do
|
40
|
+
responses = fetch_responses_once(table_name, latest_src_pos)
|
41
|
+
break if responses.nil? || responses.empty?
|
42
|
+
responses.each do |response|
|
43
|
+
yield response
|
44
|
+
end
|
45
|
+
break if responses.last.new_source_pos >= latest_src_pos
|
46
|
+
sleep interval # to avoid rush
|
47
|
+
end
|
48
|
+
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Override
|
53
|
+
# Return a resource client object
|
54
|
+
# ex: pg_client, mysql_connection
|
55
|
+
def create_resource_client(context)
|
56
|
+
raise "Not implemented"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Override
|
60
|
+
# Fetch the resource and return a response object
|
61
|
+
# Returned object must be kind of Flydata::QueryBasedSync::Response
|
62
|
+
def fetch_responses_once(table_name, latest_src_pos)
|
63
|
+
raise "Not implemented"
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'flydata/query_based_sync/record_size_estimator'
|
2
|
+
|
3
|
+
module Flydata
|
4
|
+
module QueryBasedSync
|
5
|
+
|
6
|
+
class Response
|
7
|
+
|
8
|
+
DEFAULT_EMIT_CHUNK_LIMIT = 64 * (1024**2) # 64mb
|
9
|
+
|
10
|
+
def initialize(context, table_name, records, query_cond = {})
|
11
|
+
@table_name = table_name
|
12
|
+
@records = records
|
13
|
+
@context = context
|
14
|
+
@query_cond = query_cond
|
15
|
+
|
16
|
+
@table_meta = context.table_meta[table_name.to_sym]
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :context, :table_name, :src_result, :query_cond
|
20
|
+
|
21
|
+
# Set records for sending as flydata sync records
|
22
|
+
# The format is an array of Hash objects
|
23
|
+
# {"1"=>"1000", "2"=>"Andy", ....}
|
24
|
+
attr_reader :records
|
25
|
+
|
26
|
+
# Store the source position for the resume
|
27
|
+
# This needs to be saved at the per-table binlog.pos after sending the records
|
28
|
+
attr_reader :new_source_pos
|
29
|
+
|
30
|
+
# Set per-table source position for resume
|
31
|
+
def set_new_source_pos(required_pk_values = false)
|
32
|
+
pk_values =
|
33
|
+
# Set pk_values if next_response exists or returned row size is the same as the specified limit size.
|
34
|
+
# It is for resuming in the middle of the transaction.
|
35
|
+
if required_pk_values
|
36
|
+
values = []
|
37
|
+
pk_positions = @table_meta[:pk_positions]
|
38
|
+
@table_meta[:primary_keys].each_with_index do |pk, index|
|
39
|
+
pk_pos = pk_positions[index]
|
40
|
+
values << { pk.to_s => @records.last[pk_pos.to_s] }
|
41
|
+
end
|
42
|
+
values
|
43
|
+
end
|
44
|
+
|
45
|
+
args =
|
46
|
+
if pk_values
|
47
|
+
[@query_cond[:from_sid], @query_cond[:to_sid], pk_values]
|
48
|
+
else
|
49
|
+
[@query_cond[:to_sid]]
|
50
|
+
end
|
51
|
+
|
52
|
+
@new_source_pos = @context.source_pos_class.new(*args)
|
53
|
+
end
|
54
|
+
|
55
|
+
def record_count
|
56
|
+
records.nil? ? 0 : records.count
|
57
|
+
end
|
58
|
+
|
59
|
+
def empty?
|
60
|
+
record_count == 0
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.create_responses(context, table_name, raw_result, query_cond = {})
|
64
|
+
emit_chunk_limit = context.params[:emit_chunk_limit] || DEFAULT_EMIT_CHUNK_LIMIT
|
65
|
+
table_meta = context.table_meta[table_name.to_sym]
|
66
|
+
record_size_estimator = RecordSizeEstimator.new(table_name, table_meta[:columns].count)
|
67
|
+
|
68
|
+
all_records = convert_result_to_hash(raw_result)
|
69
|
+
responses = []
|
70
|
+
|
71
|
+
if all_records.nil? || all_records.empty?
|
72
|
+
res = self.new(context, table_name, [], query_cond)
|
73
|
+
res.set_new_source_pos
|
74
|
+
return [res]
|
75
|
+
end
|
76
|
+
|
77
|
+
start_index = 0
|
78
|
+
cur_chunk_size = 0
|
79
|
+
|
80
|
+
# Split records and create response object
|
81
|
+
all_records.each_with_index do |r, i|
|
82
|
+
record_size = record_size_estimator.calc_record_size(r)
|
83
|
+
# Set another response object, if the chunk size exceeds the emit_chunk_limit
|
84
|
+
if cur_chunk_size > 0 && (cur_chunk_size + record_size) > emit_chunk_limit
|
85
|
+
responses << self.new(context, table_name, all_records[start_index..i-1], query_cond)
|
86
|
+
start_index = i
|
87
|
+
cur_chunk_size = record_size
|
88
|
+
else
|
89
|
+
cur_chunk_size += record_size
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
if cur_chunk_size > 0
|
94
|
+
responses << self.new(context, table_name, all_records[start_index..-1], query_cond)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Set new source positions for each response
|
98
|
+
responses[0..-2].each{|res| res.set_new_source_pos(true)}
|
99
|
+
more_response = (all_records.size == table_meta[:max_num_rows_per_query])
|
100
|
+
responses.last.set_new_source_pos(more_response)
|
101
|
+
|
102
|
+
responses
|
103
|
+
end
|
104
|
+
|
105
|
+
# Convert to sync record style
|
106
|
+
# ex)
|
107
|
+
# input: {"id"=>1, "name"=>"Andy"}
|
108
|
+
# output: {"1"=>1, "2"=>"Andy"}
|
109
|
+
def self.convert_result_to_hash(result)
|
110
|
+
first_record = result.first
|
111
|
+
return result if first_record.kind_of?(Hash) && first_record.has_key?('1')
|
112
|
+
|
113
|
+
result.collect do |r|
|
114
|
+
h = {}
|
115
|
+
r.values.each.with_index(1){|v, i| h[i.to_s] = v }
|
116
|
+
h
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'flydata/plugin_support/sync_record_emittable'
|
2
|
+
|
3
|
+
module Flydata
|
4
|
+
module QueryBasedSync
|
5
|
+
|
6
|
+
class ResponseHandler
|
7
|
+
include Flydata::PluginSupport::SyncRecordEmittable
|
8
|
+
|
9
|
+
def initialize(context)
|
10
|
+
@context = context
|
11
|
+
end
|
12
|
+
|
13
|
+
def handle(response)
|
14
|
+
records = response.records.collect{|r| {row: r} }
|
15
|
+
|
16
|
+
emit_sync_records(records,
|
17
|
+
type: :update, # upsert only
|
18
|
+
src_pos: response.new_source_pos.to_s,
|
19
|
+
table: response.table_name)
|
20
|
+
|
21
|
+
# TODO: Revisit and check if transaction management is needed
|
22
|
+
# However, since we use 'upsert' for query based sync,
|
23
|
+
# duplicate event will not cause an issue.
|
24
|
+
context.table_src_pos_files[response.table_name.to_sym].
|
25
|
+
save(response.new_source_pos)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -41,7 +41,7 @@ class SyncGenerateTableDdl < Component
|
|
41
41
|
if missing_tables
|
42
42
|
missing_tables.each {|missing_table| error_list << { error: "table does not exist in the #{data_source_type_display_name}", table: missing_table } }
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
[flydata_tabledefs, error_list]
|
46
46
|
end
|
47
47
|
|
@@ -24,14 +24,14 @@ module PluginSupport
|
|
24
24
|
|
25
25
|
class FlydataBinlogRecordDispatcher < BinlogRecordDispatcher
|
26
26
|
def initialize(context)
|
27
|
-
context.
|
27
|
+
context.cur_src_pos_file = ""
|
28
28
|
@context = context
|
29
29
|
@query_dispatcher = FlydataBinlogQueryDispatcher.new(context)
|
30
30
|
@dml_record_handler = DmlRecordHandler.new(context)
|
31
31
|
end
|
32
32
|
|
33
33
|
def on_rotate(record)
|
34
|
-
@context.
|
34
|
+
@context.cur_src_pos_file = record["binlog_file"]
|
35
35
|
end
|
36
36
|
|
37
37
|
def on_write_rows(record)
|
@@ -16,19 +16,13 @@ module PluginSupport
|
|
16
16
|
@table_meta = @context.table_meta
|
17
17
|
|
18
18
|
# Load per-table binlog position
|
19
|
-
@table_binlog_pos =
|
20
|
-
@context.tables.each do |table_name|
|
21
|
-
table_binlog_pos_str = @context.sync_fm.get_table_source_raw_pos(table_name)
|
22
|
-
if table_binlog_pos_str
|
23
|
-
@table_binlog_pos[table_name] = FlydataCore::Mysql::BinlogPos.new(table_binlog_pos_str)
|
24
|
-
end
|
25
|
-
end
|
19
|
+
@table_binlog_pos = @context.table_binlog_pos
|
26
20
|
end
|
27
21
|
|
28
22
|
private
|
29
23
|
|
30
24
|
def binlog_pos(record)
|
31
|
-
"#{@context.
|
25
|
+
"#{@context.cur_src_pos_file}\t#{record['next_position'] - record['event_length']}"
|
32
26
|
end
|
33
27
|
|
34
28
|
def supported_database
|
@@ -81,7 +75,7 @@ module PluginSupport
|
|
81
75
|
|
82
76
|
def check_empty_binlog
|
83
77
|
#Log one warning per consecutive records that have empty binlog filename
|
84
|
-
if @context.
|
78
|
+
if @context.cur_src_pos_file.to_s.empty?
|
85
79
|
if @first_empty_binlog
|
86
80
|
$log.warn "Binlog file name is empty. Rotate event not received!"
|
87
81
|
@first_empty_binlog = false
|
@@ -1,7 +1,31 @@
|
|
1
1
|
require 'flydata/plugin_support/context'
|
2
|
+
require 'flydata-core/mysql/binlog_pos'
|
3
|
+
|
4
|
+
module Flydata
|
5
|
+
module SourceMysql
|
6
|
+
module PluginSupport
|
2
7
|
|
3
|
-
module Flydata::SourceMysql::PluginSupport
|
4
8
|
class Context < ::Flydata::PluginSupport::Context
|
5
|
-
register_mandatory_opts :database
|
9
|
+
register_mandatory_opts :database
|
10
|
+
|
11
|
+
def initialize(opts)
|
12
|
+
super
|
13
|
+
set_table_binlog_pos
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :table_binlog_pos
|
17
|
+
|
18
|
+
def set_table_binlog_pos
|
19
|
+
@table_binlog_pos = {}
|
20
|
+
tables.each do |table_name|
|
21
|
+
table_binlog_pos_str = sync_fm.get_table_source_raw_pos(table_name)
|
22
|
+
if table_binlog_pos_str
|
23
|
+
@table_binlog_pos[table_name] = ::FlydataCore::Mysql::BinlogPos.load(table_binlog_pos_str)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
6
27
|
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
7
31
|
end
|