flydata 0.6.14 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/flydata-core/Gemfile +1 -0
  4. data/flydata-core/Gemfile.lock +5 -0
  5. data/flydata-core/lib/flydata-core/errors.rb +4 -2
  6. data/flydata-core/lib/flydata-core/mysql/binlog_pos.rb +4 -0
  7. data/flydata-core/lib/flydata-core/postgresql/compatibility_checker.rb +119 -0
  8. data/flydata-core/lib/flydata-core/postgresql/config.rb +58 -0
  9. data/flydata-core/lib/flydata-core/postgresql/pg_client.rb +170 -0
  10. data/flydata-core/lib/flydata-core/postgresql/snapshot.rb +49 -0
  11. data/flydata-core/lib/flydata-core/postgresql/source_pos.rb +71 -10
  12. data/flydata-core/lib/flydata-core/table_def/mysql_table_def.rb +1 -1
  13. data/flydata-core/lib/flydata-core/table_def/postgresql_table_def.rb +76 -17
  14. data/flydata-core/lib/flydata-core/table_def/redshift_table_def.rb +59 -10
  15. data/flydata-core/spec/mysql/binlog_pos_spec.rb +10 -2
  16. data/flydata-core/spec/postgresql/compatibility_checker_spec.rb +148 -0
  17. data/flydata-core/spec/postgresql/config_spec.rb +85 -0
  18. data/flydata-core/spec/postgresql/pg_client_spec.rb +195 -0
  19. data/flydata-core/spec/postgresql/snapshot_spec.rb +55 -0
  20. data/flydata-core/spec/postgresql/source_pos_spec.rb +70 -8
  21. data/flydata-core/spec/table_def/postgresql_table_def_spec.rb +80 -19
  22. data/flydata-core/spec/table_def/redshift_table_def_spec.rb +211 -14
  23. data/flydata.gemspec +0 -0
  24. data/lib/flydata.rb +1 -0
  25. data/lib/flydata/command/sender.rb +10 -7
  26. data/lib/flydata/command/sync.rb +4 -1
  27. data/lib/flydata/fluent-plugins/flydata_plugin_ext/base.rb +1 -0
  28. data/lib/flydata/fluent-plugins/flydata_plugin_ext/fluent_log_ext.rb +73 -0
  29. data/lib/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync.rb +35 -10
  30. data/lib/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_diff_based.rb +29 -0
  31. data/lib/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_query_based.rb +26 -0
  32. data/lib/flydata/fluent-plugins/flydata_plugin_ext/preference.rb +29 -13
  33. data/lib/flydata/fluent-plugins/in_mysql_binlog_flydata.rb +10 -18
  34. data/lib/flydata/fluent-plugins/in_postgresql_query_based_flydata.rb +64 -0
  35. data/lib/flydata/helpers.rb +1 -3
  36. data/lib/flydata/plugin_support/context.rb +14 -2
  37. data/lib/flydata/plugin_support/source_position_file.rb +35 -0
  38. data/lib/flydata/plugin_support/sync_record_emittable.rb +2 -1
  39. data/lib/flydata/query_based_sync/client.rb +101 -0
  40. data/lib/flydata/query_based_sync/record_size_estimator.rb +39 -0
  41. data/lib/flydata/query_based_sync/resource_requester.rb +70 -0
  42. data/lib/flydata/query_based_sync/response.rb +122 -0
  43. data/lib/flydata/query_based_sync/response_handler.rb +30 -0
  44. data/lib/flydata/source/sync_generate_table_ddl.rb +1 -1
  45. data/lib/flydata/source_mysql/plugin_support/binlog_record_dispatcher.rb +2 -2
  46. data/lib/flydata/source_mysql/plugin_support/binlog_record_handler.rb +3 -9
  47. data/lib/flydata/source_mysql/plugin_support/context.rb +26 -2
  48. data/lib/flydata/source_mysql/plugin_support/source_position_file.rb +14 -0
  49. data/lib/flydata/source_mysql/table_ddl.rb +3 -3
  50. data/lib/flydata/source_mysql/{plugin_support/table_meta.rb → table_meta.rb} +3 -10
  51. data/lib/flydata/source_postgresql/generate_source_dump.rb +44 -63
  52. data/lib/flydata/source_postgresql/parse_dump_and_send.rb +2 -0
  53. data/lib/flydata/source_postgresql/plugin_support/context.rb +13 -0
  54. data/lib/flydata/source_postgresql/plugin_support/source_position_file.rb +14 -0
  55. data/lib/flydata/source_postgresql/query_based_sync/client.rb +16 -0
  56. data/lib/flydata/source_postgresql/query_based_sync/diff_query_generator.rb +135 -0
  57. data/lib/flydata/source_postgresql/query_based_sync/resource_requester.rb +86 -0
  58. data/lib/flydata/source_postgresql/query_based_sync/response.rb +12 -0
  59. data/lib/flydata/source_postgresql/query_based_sync/response_handler.rb +12 -0
  60. data/lib/flydata/source_postgresql/sync_generate_table_ddl.rb +25 -79
  61. data/lib/flydata/source_postgresql/table_meta.rb +168 -0
  62. data/lib/flydata/sync_file_manager.rb +5 -5
  63. data/lib/flydata/table_meta.rb +19 -0
  64. data/spec/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_context.rb +85 -0
  65. data/spec/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_diff_based_shared_examples.rb +36 -0
  66. data/spec/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_query_based_shared_examples.rb +37 -0
  67. data/spec/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_shared_examples.rb +67 -0
  68. data/spec/flydata/fluent-plugins/in_mysql_binlog_flydata_spec.rb +119 -96
  69. data/spec/flydata/fluent-plugins/in_postgresql_query_based_flydata_spec.rb +82 -0
  70. data/spec/flydata/fluent-plugins/sync_source_plugin_context.rb +29 -0
  71. data/spec/flydata/plugin_support/context_spec.rb +37 -3
  72. data/spec/flydata/query_based_sync/client_spec.rb +79 -0
  73. data/spec/flydata/query_based_sync/query_based_sync_context.rb +116 -0
  74. data/spec/flydata/query_based_sync/record_size_estimator_spec.rb +54 -0
  75. data/spec/flydata/query_based_sync/resource_requester_spec.rb +58 -0
  76. data/spec/flydata/query_based_sync/response_handler_spec.rb +36 -0
  77. data/spec/flydata/query_based_sync/response_spec.rb +157 -0
  78. data/spec/flydata/source_mysql/plugin_support/context_spec.rb +7 -1
  79. data/spec/flydata/source_mysql/plugin_support/dml_record_handler_spec.rb +2 -15
  80. data/spec/flydata/source_mysql/plugin_support/drop_database_query_handler_spec.rb +1 -1
  81. data/spec/flydata/source_mysql/plugin_support/shared_query_handler_context.rb +12 -11
  82. data/spec/flydata/source_mysql/plugin_support/source_position_file_spec.rb +53 -0
  83. data/spec/flydata/source_mysql/plugin_support/truncate_query_handler_spec.rb +1 -1
  84. data/spec/flydata/source_mysql/table_ddl_spec.rb +5 -5
  85. data/spec/flydata/source_mysql/{plugin_support/table_meta_spec.rb → table_meta_spec.rb} +6 -7
  86. data/spec/flydata/source_postgresql/generate_source_dump_spec.rb +165 -77
  87. data/spec/flydata/source_postgresql/query_based_sync/diff_query_generator_spec.rb +213 -0
  88. data/spec/flydata/source_postgresql/query_based_sync/query_based_sync_postgresql_context.rb +76 -0
  89. data/spec/flydata/source_postgresql/query_based_sync/resource_requester_spec.rb +70 -0
  90. data/spec/flydata/source_postgresql/table_meta_spec.rb +77 -0
  91. metadata +49 -6
  92. data/lib/flydata/source_mysql/plugin_support/binlog_position_file.rb +0 -23
  93. 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.current_binlog_file = ""
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.current_binlog_file = record["binlog_file"]
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.current_binlog_file}\t#{record['next_position'] - record['event_length']}"
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.current_binlog_file.to_s.empty?
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, :table_meta
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