flydata 0.6.14 → 0.7.0

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.
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