flydata 0.6.3 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -2
  3. data/VERSION +1 -1
  4. data/bin/fdredshift +78 -0
  5. data/circle.yml +1 -1
  6. data/ext/flydata/{parser/mysql → source_mysql/parser}/.gitignore +0 -0
  7. data/ext/flydata/{parser/mysql → source_mysql/parser}/dump_parser_ext.cpp +3 -3
  8. data/ext/flydata/source_mysql/parser/extconf.rb +3 -0
  9. data/ext/flydata/{parser/mysql → source_mysql/parser}/parser.txt +0 -0
  10. data/ext/flydata/{parser/mysql → source_mysql/parser}/sql_parser.cpp +0 -0
  11. data/ext/flydata/{parser/mysql → source_mysql/parser}/sql_parser.h +0 -0
  12. data/flydata-core/lib/flydata-core/mysql/binlog_pos.rb +34 -32
  13. data/flydata-core/lib/flydata-core/mysql/compatibility_checker.rb +20 -0
  14. data/flydata-core/lib/flydata-core/table_def/mysql_table_def.rb +12 -4
  15. data/flydata-core/lib/flydata-core/table_def/redshift_table_def.rb +60 -6
  16. data/flydata-core/spec/mysql/binlog_pos_spec.rb +474 -0
  17. data/flydata-core/spec/table_def/mysql_table_def_spec.rb +57 -0
  18. data/flydata-core/spec/table_def/mysql_to_redshift_table_def_spec.rb +174 -20
  19. data/flydata-core/spec/table_def/mysqldump_test_col_comment_with_AUTO_INCREMENT_keyword.dump +43 -0
  20. data/flydata-core/spec/table_def/mysqldump_test_col_comment_with_not_null_keyword.dump +43 -0
  21. data/flydata-core/spec/table_def/mysqldump_test_col_comment_with_unique_keyword.dump +43 -0
  22. data/flydata-core/spec/table_def/mysqldump_test_col_comment_with_unsigned_keyword.dump +43 -0
  23. data/flydata-core/spec/table_def/redshift_table_def_spec.rb +41 -8
  24. data/flydata.gemspec +0 -0
  25. data/lib/flydata/cli.rb +11 -5
  26. data/lib/flydata/command/base.rb +14 -1
  27. data/lib/flydata/command/exclusive_runnable.rb +42 -12
  28. data/lib/flydata/command/helper.rb +6 -6
  29. data/lib/flydata/command/sender.rb +4 -3
  30. data/lib/flydata/command/setup.rb +30 -381
  31. data/lib/flydata/command/stop.rb +1 -0
  32. data/lib/flydata/command/sync.rb +273 -301
  33. data/lib/flydata/compatibility_check.rb +24 -117
  34. data/lib/flydata/fluent-plugins/in_mysql_binlog_flydata.rb +3 -3
  35. data/lib/flydata/fluent-plugins/mysql/alter_table_query_handler.rb +2 -2
  36. data/lib/flydata/fluent-plugins/mysql/binlog_record_handler.rb +6 -6
  37. data/lib/flydata/fluent-plugins/mysql/truncate_table_query_handler.rb +0 -1
  38. data/lib/flydata/parser.rb +14 -0
  39. data/lib/flydata/{parser_provider.rb → parser/parser_provider.rb} +6 -4
  40. data/lib/flydata/parser/source_table.rb +33 -0
  41. data/lib/flydata/source.rb +105 -0
  42. data/lib/flydata/source/component.rb +21 -0
  43. data/lib/flydata/source/errors.rb +7 -0
  44. data/lib/flydata/source/generate_source_dump.rb +72 -0
  45. data/lib/flydata/source/parse_dump_and_send.rb +52 -0
  46. data/lib/flydata/source/setup.rb +31 -0
  47. data/lib/flydata/source/source_pos.rb +45 -0
  48. data/lib/flydata/source/sync.rb +56 -0
  49. data/lib/flydata/source/sync_generate_table_ddl.rb +43 -0
  50. data/lib/flydata/source_file/setup.rb +17 -0
  51. data/lib/flydata/source_file/sync.rb +14 -0
  52. data/lib/flydata/{command → source_mysql/command}/mysql.rb +2 -1
  53. data/lib/flydata/{command → source_mysql/command}/mysql_command_base.rb +2 -4
  54. data/lib/flydata/{command → source_mysql/command}/mysqlbinlog.rb +2 -1
  55. data/lib/flydata/{command → source_mysql/command}/mysqldump.rb +2 -1
  56. data/lib/flydata/source_mysql/generate_source_dump.rb +53 -0
  57. data/lib/flydata/source_mysql/mysql_compatibility_check.rb +114 -0
  58. data/lib/flydata/source_mysql/parse_dump_and_send.rb +28 -0
  59. data/lib/flydata/{parser/mysql → source_mysql/parser}/.gitignore +0 -0
  60. data/lib/flydata/{parser/mysql → source_mysql/parser}/dump_parser.rb +32 -67
  61. data/lib/flydata/{parser/mysql → source_mysql/parser}/mysql_alter_table.treetop +0 -0
  62. data/lib/flydata/source_mysql/setup.rb +24 -0
  63. data/lib/flydata/source_mysql/source_pos.rb +21 -0
  64. data/lib/flydata/source_mysql/sync.rb +45 -0
  65. data/lib/flydata/source_mysql/sync_generate_table_ddl.rb +40 -0
  66. data/lib/flydata/{mysql → source_mysql}/table_ddl.rb +6 -17
  67. data/lib/flydata/source_zendesk/sync_generate_table_ddl.rb +30 -0
  68. data/lib/flydata/source_zendesk/zendesk_flydata_tabledefs.rb +133 -0
  69. data/lib/flydata/sync_file_manager.rb +132 -73
  70. data/lib/flydata/table_ddl.rb +18 -0
  71. data/spec/flydata/cli_spec.rb +1 -0
  72. data/spec/flydata/command/exclusive_runnable_spec.rb +19 -8
  73. data/spec/flydata/command/sender_spec.rb +1 -1
  74. data/spec/flydata/command/setup_spec.rb +4 -4
  75. data/spec/flydata/command/sync_spec.rb +97 -134
  76. data/spec/flydata/compatibility_check_spec.rb +16 -289
  77. data/spec/flydata/fluent-plugins/mysql/alter_table_query_handler_spec.rb +3 -3
  78. data/spec/flydata/fluent-plugins/mysql/dml_record_handler_spec.rb +1 -1
  79. data/spec/flydata/fluent-plugins/mysql/shared_query_handler_context.rb +4 -2
  80. data/spec/flydata/fluent-plugins/mysql/truncate_query_handler_spec.rb +1 -1
  81. data/spec/flydata/source_mysql/generate_source_dump_spec.rb +69 -0
  82. data/spec/flydata/source_mysql/mysql_compatibility_check_spec.rb +280 -0
  83. data/spec/flydata/{parser/mysql → source_mysql/parser}/alter_table_parser_spec.rb +2 -2
  84. data/spec/flydata/{parser/mysql → source_mysql/parser}/dump_parser_spec.rb +75 -70
  85. data/spec/flydata/source_mysql/sync_generate_table_ddl_spec.rb +137 -0
  86. data/spec/flydata/{mysql → source_mysql}/table_ddl_spec.rb +2 -2
  87. data/spec/flydata/source_spec.rb +140 -0
  88. data/spec/flydata/source_zendesk/sync_generate_table_ddl_spec.rb +33 -0
  89. data/spec/flydata/sync_file_manager_spec.rb +157 -77
  90. data/tmpl/redshift_mysql_data_entry.conf.tmpl +1 -1
  91. metadata +56 -23
  92. data/ext/flydata/parser/mysql/extconf.rb +0 -3
  93. data/lib/flydata/mysql/binlog_position.rb +0 -22
  94. data/spec/flydata/mysql/binlog_position_spec.rb +0 -35
@@ -1,22 +1,11 @@
1
- require 'flydata/mysql/binlog_position'
1
+ require 'flydata/table_ddl'
2
2
  require 'flydata-core/mysql/command_generator'
3
+ require 'flydata-core/mysql/binlog_pos'
3
4
 
4
5
  module Flydata
5
- module Mysql
6
+ module SourceMysql
6
7
 
7
8
  class TableDdl
8
- VERSION0 = 0 # the version where no .generated_ddl file was generated.
9
- # Therefore, this version never shows up in anywhere.
10
- VERSION1 = 1 # the version which doesn't handle server side encoding support.
11
- VERSION2 = 2 # the version with server side encoding support, migrated from
12
- # the previous versions. Format/functionality-wise, it's the
13
- # same as Version 3.
14
- VERSION3 = 3 # the version with server side encoding support, generated by
15
- # sync:generated_table_ddl command.
16
- VERSION4 = 4 # the version with server side encoding support, generated by
17
- # the auto-generated CREATE TABLE event.
18
- VERSION = VERSION3
19
-
20
9
  def self.migrate_tables(tables, mysql_opts, sync_fm, position_file, context,
21
10
  &block)
22
11
  migrate_to_v2(tables, mysql_opts, sync_fm, position_file, context, &block)
@@ -24,7 +13,7 @@ class TableDdl
24
13
 
25
14
  private
26
15
 
27
- V2_TARGET_VERSION = VERSION2
16
+ V2_TARGET_VERSION = Flydata::TableDdl::VERSION2
28
17
  EVENT_TYPE = 'Query'
29
18
  ALTER_TABLE_CHARSET_SQL = <<EOS.strip
30
19
  ALTER TABLE `%s`.`%s` CHARACTER SET = %s;
@@ -60,9 +49,9 @@ EOS
60
49
  mysql_tabledef = mysql_tabledefs[table]
61
50
  if binlog_pos.nil?
62
51
  # get binlog position
63
- binlog_pos = BinLogPosition.new(File.open(position_file){|f| f.read })
52
+ binlog_pos = FlydataCore::Mysql::BinlogPos.new(File.open(position_file){|f| f.read })
64
53
  original_binlog_file = context.current_binlog_file
65
- context.current_binlog_file = binlog_pos.file
54
+ context.current_binlog_file = binlog_pos.filename
66
55
  end
67
56
  # get charset
68
57
  charset = mysql_tabledef.default_charset_mysql
@@ -0,0 +1,30 @@
1
+ require 'flydata/source/sync_generate_table_ddl'
2
+ require 'flydata/source_zendesk/zendesk_flydata_tabledefs'
3
+
4
+ module Flydata
5
+ module SourceZendesk
6
+
7
+ class SyncGenerateTableDdl < Source::SyncGenerateTableDdl
8
+ def run_compatibility_check
9
+ #TODO Check version "Zendesk v2 API"
10
+ end
11
+
12
+ def generate_flydata_tabledef(tables, options)
13
+ flydata_tabledefs = []
14
+ error_list = []
15
+ zendesk_resource_names = ZENDESK_FLYDATA_TABLEDEFS.keys.join(" ")
16
+
17
+ tables.each do |tablename|
18
+ flydata_tabledef = ZENDESK_FLYDATA_TABLEDEFS[tablename]
19
+ if !flydata_tabledef
20
+ error_list << { error: "Invalid Zendesk resource name specified. Flydata supports following Zendesk resources: #{zendesk_resource_names}", table: tablename }
21
+ next
22
+ end
23
+ flydata_tabledefs << flydata_tabledef
24
+ end
25
+ [flydata_tabledefs, error_list]
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,133 @@
1
+ module Flydata
2
+ module SourceZendesk
3
+
4
+ class SyncGenerateTableDdl < Source::SyncGenerateTableDdl
5
+ ZENDESK_FLYDATA_TABLEDEFS =
6
+ {"tickets"=>
7
+ {:table_name=>"tickets",
8
+ :columns=>
9
+ [{:column=>"id", :type=>"int8(20)", :not_null=>true, :primary_key=>true},
10
+ {:column=>"url", :type=>"varchar(8000)", :default=>nil},#RFC7231 8000 octets
11
+ {:column=>"external_id", :type=>"varchar(2047)", :default=>nil},
12
+ {:column=>"type", :type=>"varchar(24)", :default=>nil},
13
+ {:column=>"subject", :type=>"text"},
14
+ {:column=>"raw_subject", :type=>"text"},
15
+ {:column=>"description", :type=>"text"},
16
+ {:column=>"priority", :type=>"varchar(24)", :default=>nil},
17
+ {:column=>"status", :type=>"varchar(24)", :default=>nil},
18
+ {:column=>"recipient", :type=>"varchar(762)", :default=>nil},#email
19
+ {:column=>"requester_id", :type=>"int8(20)", :default=>nil},
20
+ {:column=>"submitter_id", :type=>"int8(20)", :default=>nil},
21
+ {:column=>"assignee_id", :type=>"int8(20)", :default=>nil},
22
+ {:column=>"organization_id", :type=>"int8(20)", :default=>nil},
23
+ {:column=>"group_id", :type=>"int8(20)", :default=>nil},
24
+ {:column=>"collaborator_ids", :type=>"text"},
25
+ {:column=>"forum_topic_id", :type=>"int8(20)", :default=>nil},
26
+ {:column=>"problem_id", :type=>"int8(20)", :default=>nil},
27
+ {:column=>"has_incidents", :type=>"int1(1)", :default=>nil},
28
+ {:column=>"due_at", :type=>"datetime", :default=>nil},
29
+ {:column=>"tags", :type=>"text"},
30
+ {:column=>"via", :type=>"text"},
31
+ {:column=>"custom_fields", :type=>"text"},
32
+ {:column=>"satisfaction_rating", :type=>"text"},
33
+ {:column=>"sharing_agreement_ids", :type=>"text"},
34
+ {:column=>"followup_ids", :type=>"text"},
35
+ {:column=>"ticket_form_id", :type=>"int8(20)", :default=>nil},
36
+ {:column=>"brand_id", :type=>"int8(20)", :default=>nil},
37
+ {:column=>"created_at", :type=>"datetime", :default=>nil},
38
+ {:column=>"updated_at", :type=>"datetime", :default=>nil}],
39
+ :default_charset=>"UTF_8",
40
+ :src_ddl=> '{"version":"Zendesk v2 API","url":"https://developer.zendesk.com/rest_api/docs/core/tickets”}'
41
+ },
42
+ "ticket_comments"=>
43
+ {:table_name=>"ticket_comments",
44
+ :columns=>
45
+ [{:column=>"id", :type=>"int8(20)", :not_null=>true, :primary_key=>true},
46
+ {:column=>"type", :type=>"varchar(24)", :default=>nil},
47
+ {:column=>"body", :type=>"text"},
48
+ {:column=>"html_body", :type=>"text"},
49
+ {:column=>"public", :type=>"int1(1)", :default=>nil},
50
+ {:column=>"author_id", :type=>"int8(20)", :default=>nil},
51
+ {:column=>"attachments", :type=>"text"},
52
+ {:column=>"via", :type=>"text"},
53
+ {:column=>"metadata", :type=>"text"},
54
+ {:column=>"created_at", :type=>"datetime", :default=>nil}],
55
+ :default_charset=>"UTF_8",
56
+ :src_ddl=> '{"version":"Zendesk v2 API","url":"https://developer.zendesk.com/rest_api/docs/core/ticket_comments”}'
57
+ },
58
+ "ticket_audits"=>
59
+ {:table_name=>"ticket_audits",
60
+ :columns=>
61
+ [{:column=>"id", :type=>"int8(20)", :not_null=>true, :primary_key=>true},
62
+ {:column=>"ticket_id", :type=>"int8(20)", :default=>nil},
63
+ {:column=>"metadata", :type=>"text"},
64
+ {:column=>"via", :type=>"text"},
65
+ {:column=>"created_at", :type=>"datetime", :default=>nil},
66
+ {:column=>"author_id", :type=>"int8(20)", :default=>nil},
67
+ {:column=>"events", :type=>"text"}],
68
+ :default_charset=>"UTF_8",
69
+ :src_ddl=> '{"version":"Zendesk v2 API","url":"https://developer.zendesk.com/rest_api/docs/core/ticket_audits”}'
70
+ },
71
+ "users"=>
72
+ {:table_name=>"users",
73
+ :columns=>
74
+ [{:column=>"id", :type=>"int8(20)", :not_null=>true, :primary_key=>true},
75
+ {:column=>"url", :type=>"varchar(8000)", :default=>nil},#RFC 7230 recommendation (8000 octets)
76
+ {:column=>"name", :type=>"varchar(6141)", :default=>nil},
77
+ {:column=>"external_id", :type=>"varchar(2047)", :default=>nil},
78
+ {:column=>"alias", :type=>"varchar(6141)", :default=>nil},
79
+ {:column=>"created_at", :type=>"datetime", :default=>nil},
80
+ {:column=>"updated_at", :type=>"datetime", :default=>nil},
81
+ {:column=>"active", :type=>"int1(1)", :default=>nil},
82
+ {:column=>"verified", :type=>"int1(1)", :default=>nil},
83
+ {:column=>"shared", :type=>"int1(1)", :default=>nil},
84
+ {:column=>"shared_agent", :type=>"int1(1)", :default=>nil},
85
+ {:column=>"locale", :type=>"varchar(765)", :default=>nil},
86
+ {:column=>"locale_id", :type=>"int4(11)", :default=>nil},
87
+ {:column=>"time_zone", :type=>"varchar(765)", :default=>nil},
88
+ {:column=>"last_login_at", :type=>"datetime", :default=>nil},
89
+ {:column=>"two_factor_auth_enabled", :type=>"int1(1)", :default=>nil},
90
+ {:column=>"email", :type=>"varchar(762)", :default=>nil},
91
+ {:column=>"phone", :type=>"varchar(765)", :default=>nil},
92
+ {:column=>"signature", :type=>"text"},
93
+ {:column=>"details", :type=>"text"},
94
+ {:column=>"notes", :type=>"text"},
95
+ {:column=>"organization_id", :type=>"int8(20)", :default=>nil},
96
+ {:column=>"role", :type=>"varchar(24)", :default=>nil},
97
+ {:column=>"custom_role_id", :type=>"int8(20)", :default=>nil},
98
+ {:column=>"moderator", :type=>"int1(1)", :default=>nil},
99
+ {:column=>"ticket_restriction", :type=>"varchar(24)", :default=>nil},
100
+ {:column=>"only_private_comments", :type=>"int1(1)", :default=>nil},
101
+ {:column=>"tags", :type=>"text"},
102
+ {:column=>"suspended", :type=>"int1(1)", :default=>nil},
103
+ {:column=>"restricted_agent", :type=>"int1(1)", :default=>nil},
104
+ {:column=>"photo", :type=>"text"},
105
+ {:column=>"user_fields", :type=>"text"}],
106
+ :default_charset=>"UTF_8",
107
+ :src_ddl=> '{"version":"Zendesk v2 API","url":"https://developer.zendesk.com/rest_api/docs/core/users”}'
108
+ },
109
+ "organizations"=>
110
+ {:table_name=>"organizations",
111
+ :columns=>
112
+ [{:column=>"id", :type=>"int8(20)", :not_null=>true, :primary_key=>true},
113
+ {:column=>"url", :type=>"varchar(8000)", :default=>nil},
114
+ {:column=>"external_id", :type=>"varchar(2047)", :default=>nil},
115
+ {:column=>"name", :type=>"varchar(6141)", :default=>nil},
116
+ {:column=>"created_at", :type=>"datetime", :default=>nil},
117
+ {:column=>"updated_at", :type=>"datetime", :default=>nil},
118
+ {:column=>"domain_names", :type=>"text"},
119
+ {:column=>"details", :type=>"text"},
120
+ {:column=>"notes", :type=>"text"},
121
+ {:column=>"group_id", :type=>"int8(20)", :default=>nil},
122
+ {:column=>"shared_tickets", :type=>"int1(1)", :default=>nil},
123
+ {:column=>"shared_comments", :type=>"int1(1)", :default=>nil},
124
+ {:column=>"tags", :type=>"text"},
125
+ {:column=>"organization_fields", :type=>"text"}],
126
+ :default_charset=>"UTF_8",
127
+ :src_ddl=> '{"version":"Zendesk v2 API","url":"https://developer.zendesk.com/rest_api/docs/core/organizations”}'
128
+ }
129
+ }
130
+ end
131
+
132
+ end
133
+ end
@@ -1,6 +1,9 @@
1
- require 'mysql2'
2
1
  require 'flydata-core/query_job'
2
+ require 'flydata/parser/source_table' # Required for Marshal.load
3
+ require 'flydata/parser'
3
4
  require 'set'
5
+ require 'flydata/source'
6
+ require 'flydata/source/source_pos'
4
7
 
5
8
  module Flydata
6
9
  class SyncFileManager
@@ -9,12 +12,17 @@ module Flydata
9
12
  TABLE_POSITIONS_DIR = ENV['FLYDATA_TABLE_POSITIONS'] || File.join(FLYDATA_HOME, 'positions')
10
13
  SYNC_TABLE_POSITIONS = 0
11
14
 
12
- def initialize(data_entry)
15
+ def initialize(data_entry, source = nil)
13
16
  @data_entry = data_entry
17
+ @source = source #for Source dependent objects
14
18
  @table_position_files = {} # File objects keyed by table name
15
19
  @sync_table_positions_count = SYNC_TABLE_POSITIONS
16
20
  end
17
21
 
22
+ def source
23
+ @source
24
+ end
25
+
18
26
  def close
19
27
  @table_position_files.values.each {|f| f.close }
20
28
  @table_position_files = {}
@@ -29,21 +37,19 @@ module Flydata
29
37
  dump_file_path + ".pos"
30
38
  end
31
39
 
32
- def save_dump_pos(status, table_name, last_pos, binlog_pos, state = nil, substate = nil)
40
+ def save_dump_pos(status, table_name, last_pos, source_pos, state = nil, substate = nil)
33
41
  File.open(dump_pos_path, 'w') do |f|
34
- f.write(dump_pos_content(status, table_name, last_pos, binlog_pos, state, substate))
42
+ f.write(dump_pos_content(status, table_name, last_pos, source_pos, state, substate))
35
43
  end
36
44
  end
37
45
 
38
46
  def load_dump_pos
39
47
  path = dump_pos_path
40
48
  return {} unless File.exists?(path)
41
- items = File.open(path, 'r').readline.split("\t")
42
- raise "Invalid dump.pos file: #{path}" unless items.length >= 5 && items.length <= 7
43
- mysql_table = load_mysql_table_marshal_dump
44
- { status: items[0], table_name: items[1], last_pos: items[2].to_i,
45
- binlog_pos: {binfile: items[3], pos: items[4].to_i},
46
- state: items[5], substate: items[6], mysql_table: mysql_table}
49
+ content = File.open(path, 'r').readline
50
+ source_table = load_source_table_marshal_dump
51
+
52
+ dump_pos_content_to_hash(content).merge( { source_table: source_table} )
47
53
  end
48
54
 
49
55
  def load_generated_ddl(tables)
@@ -79,60 +85,73 @@ module Flydata
79
85
  new_tables
80
86
  end
81
87
 
82
- # MysqlTable marshal file
83
- def mysql_table_marshal_dump_path
84
- dump_file_path + ".mysql_table"
88
+ SOURCE_TABLE_EXT = "mysql_table"
89
+
90
+ # SourceTable marshal file
91
+ def source_table_marshal_dump_path
92
+ dump_file_path + ".#{SOURCE_TABLE_EXT}"
85
93
  end
86
94
 
87
- def save_mysql_table_marshal_dump(mysql_table)
88
- File.open(mysql_table_marshal_dump_path, 'w') do |f|
89
- f.write Marshal.dump(mysql_table)
95
+ def save_source_table_marshal_dump(source_table)
96
+ File.open(source_table_marshal_dump_path, 'w') do |f|
97
+ f.write Marshal.dump(source_table)
90
98
  end
91
99
  end
92
100
 
93
101
  # master binlog.pos file
94
- def save_binlog(binlog_pos)
95
- path = binlog_path
102
+ def save_source_pos(source_pos)
103
+ path = source_pos_path
96
104
  File.open(path, 'w') do |f|
97
- f.write(binlog_content(binlog_pos))
105
+ f.write(source_pos.to_s)
98
106
  end
99
107
  end
100
108
 
101
- def load_binlog(file_path = binlog_path)
109
+ def load_source_pos(file_path = source_pos_path)
102
110
  return nil unless File.exists?(file_path)
103
- f, pos = IO.read(file_path).strip.split("\t")
104
- return nil if f.nil? || f.empty? || pos.nil?
105
- { binfile: f, pos: pos.to_i }
111
+
112
+ source_pos_str = IO.read(file_path).strip
113
+ begin
114
+ context = source.source_pos
115
+ source_pos = context.create_source_pos(source_pos_str)
116
+ rescue RuntimeError
117
+ return nil
118
+ end
119
+ source_pos
106
120
  end
107
121
 
108
- def binlog_path
122
+ def source_pos_path
109
123
  File.join(FLYDATA_HOME, @data_entry['name'] + ".binlog.pos")
110
124
  end
111
125
 
112
- # sent binlog.pos file
113
- def save_sent_binlog(binlog_pos)
114
- File.open(sent_binlog_path, 'w') do |f|
115
- f.write(binlog_content(binlog_pos))
126
+ # sent source pos file (binlog.pos)
127
+ def save_sent_source_pos(source_pos)
128
+ File.open(sent_source_pos_path, 'w') do |f|
129
+ f.write(source_pos.to_s)
116
130
  end
117
131
  end
118
132
 
119
- def load_sent_binlog(file_path = sent_binlog_path)
133
+ def load_sent_source_pos(file_path = sent_source_pos_path)
120
134
  return nil unless File.exists?(file_path)
121
- f, pos = IO.read(file_path).strip.split("\t")
122
- return nil if f.nil? || f.empty? || pos.nil?
123
- { binfile: f, pos: pos.to_i }
135
+
136
+ source_pos_str = IO.read(file_path).strip
137
+ begin
138
+ source_pos = source.source_pos.create_source_pos(source_pos_str)
139
+ rescue RuntimeError
140
+ return nil
141
+ end
142
+ source_pos
124
143
  end
125
144
 
126
- def sent_binlog_path(master_binlog_path = binlog_path)
127
- validate_master_binlog_path(master_binlog_path)
128
- "#{master_binlog_path[0..-5]}.sent.pos"
145
+ def sent_source_pos_path(master_source_pos_path = source_pos_path)
146
+ validate_master_source_pos_path(master_source_pos_path)
147
+ "#{master_source_pos_path[0..-5]}.sent.pos"
129
148
  end
130
149
 
131
150
  # ssl_ca file path
132
- def ssl_ca_path(master_binlog_path = binlog_path)
133
- validate_master_binlog_path(master_binlog_path)
151
+ def ssl_ca_path(master_source_pos_path = source_pos_path)
152
+ validate_master_source_pos_path(master_source_pos_path)
134
153
  # <data-entry-name>.ssl_ca.pem
135
- "#{master_binlog_path[0..-12]}.ssl_ca.pem"
154
+ "#{master_source_pos_path[0..-12]}.ssl_ca.pem"
136
155
  end
137
156
 
138
157
  def save_ssl_ca(ssl_ca_content, path = ssl_ca_path)
@@ -142,10 +161,10 @@ module Flydata
142
161
  end
143
162
 
144
163
  # ssl_cipher file path
145
- def ssl_cipher_path(master_binlog_path = binlog_path)
146
- validate_master_binlog_path(master_binlog_path)
164
+ def ssl_cipher_path(master_source_pos_path = source_pos_path)
165
+ validate_master_source_pos_path(master_source_pos_path)
147
166
  # <data-entry-name>.ssl_cipher
148
- "#{master_binlog_path[0..-12]}.ssl_cipher"
167
+ "#{master_source_pos_path[0..-12]}.ssl_cipher"
149
168
  end
150
169
 
151
170
  def save_ssl_cipher(ssl_cipher_content, path = ssl_cipher_path)
@@ -176,12 +195,12 @@ module Flydata
176
195
  tables.map{|table| File.join(table_positions_dir_path, table + '.generated_ddl')}
177
196
  end
178
197
 
179
- def table_binlog_pos_paths(*tables)
198
+ def table_source_pos_paths(*tables)
180
199
  tables.empty? ? Dir.glob(File.join(table_positions_dir_path, '*.binlog.pos')) :
181
200
  tables.map{|table| File.join(table_positions_dir_path, table + '.binlog.pos')}
182
201
  end
183
202
 
184
- def table_binlog_pos_init_paths(*tables)
203
+ def table_source_pos_init_paths(*tables)
185
204
  tables.empty? ? Dir.glob(File.join(table_positions_dir_path, '*.binlog.pos.init')) :
186
205
  tables.map{|table| File.join(table_positions_dir_path, table + '.binlog.pos.init')}
187
206
  end
@@ -262,10 +281,11 @@ module Flydata
262
281
  tables: items[1].split(" ") }
263
282
  end
264
283
 
265
- def get_table_binlog_pos_init(table_name)
284
+ def get_table_source_pos_init(table_name)
266
285
  file = File.join(table_positions_dir_path, table_name + ".binlog.pos.init")
267
286
  return nil unless File.exists?(file)
268
- File.open(file, 'r').readline
287
+
288
+ source.source_pos.create_source_pos( File.open(file, 'r').readline )
269
289
  end
270
290
 
271
291
  def table_rev_file_path(table_name)
@@ -302,8 +322,8 @@ module Flydata
302
322
  def delete_table_control_files(*tables)
303
323
  files_to_delete = [
304
324
  table_position_file_paths(*tables),
305
- table_binlog_pos_paths(*tables),
306
- table_binlog_pos_init_paths(*tables),
325
+ table_source_pos_paths(*tables),
326
+ table_source_pos_init_paths(*tables),
307
327
  table_rev_file_paths(*tables),
308
328
  table_ddl_file_paths(*tables)
309
329
  ]
@@ -327,7 +347,7 @@ module Flydata
327
347
  files_to_delete = [
328
348
  dump_file_path,
329
349
  dump_pos_path,
330
- mysql_table_marshal_dump_path,
350
+ source_table_marshal_dump_path,
331
351
  sync_info_file,
332
352
  stats_path
333
353
  ]
@@ -338,8 +358,8 @@ module Flydata
338
358
 
339
359
  def delete_master_position_files
340
360
  files_to_delete = [
341
- binlog_path,
342
- sent_binlog_path,
361
+ source_pos_path,
362
+ sent_source_pos_path,
343
363
  lock_pid_file,
344
364
  ]
345
365
  files_to_delete.flatten.each do |file_to_delete|
@@ -348,7 +368,7 @@ module Flydata
348
368
  end
349
369
 
350
370
 
351
- def delete_table_binlog_pos(table_name)
371
+ def delete_table_source_pos(table_name)
352
372
  file = File.join(table_positions_dir_path, table_name + ".binlog.pos")
353
373
  if File.exists?(file)
354
374
  FileUtils.rm(file, :force => true)
@@ -357,7 +377,7 @@ module Flydata
357
377
  end
358
378
  end
359
379
 
360
- def save_table_binlog_pos(tables, binlog_pos, options = {})
380
+ def save_table_source_pos(tables, source_pos, options = {})
361
381
  dest_dir = case options[:destination]
362
382
  when :positions; table_positions_dir_path
363
383
  when :dump; dump_dir
@@ -368,18 +388,28 @@ module Flydata
368
388
  tables.each do |table_name|
369
389
  file = File.join(dest_dir, table_name + ".binlog.pos")
370
390
  File.open(file, "w") do |f|
371
- f.write(binlog_content(binlog_pos))
391
+ f.write(source_pos.to_s)
372
392
  end
373
393
  end
374
394
  end
375
395
 
376
- def get_table_binlog_pos(table_name)
396
+ def get_table_source_pos(table_name)
397
+ source_pos_str = get_table_source_raw_pos(table_name)
398
+ return nil unless source_pos_str
399
+
400
+ source.source_pos.create_source_pos( source_pos_str )
401
+ end
402
+
403
+ def get_table_source_raw_pos(table_name) #returns String. interface for fluentd
377
404
  file = File.join(table_positions_dir_path, table_name + ".binlog.pos")
378
405
  return nil unless File.exists?(file)
406
+
379
407
  File.open(file, 'r').readline
380
408
  end
381
409
 
382
- def install_table_binlog_files(tables)
410
+
411
+
412
+ def install_table_source_pos_files(tables)
383
413
  FileUtils.mkdir_p(table_positions_dir_path) unless Dir.exists?(table_positions_dir_path)
384
414
  tables.each do |table_name|
385
415
  file_name = table_name + ".binlog.pos"
@@ -403,7 +433,7 @@ module Flydata
403
433
  FileUtils.mkdir_p(backup_dir) unless Dir.exists?(backup_dir)
404
434
  dest_dir = File.join(backup_dir, Time.now.strftime("%Y%m%d%H%M%S"))
405
435
  FileUtils.mkdir(dest_dir)
406
- %w(info pos stats mysql_table).each do |ext|
436
+ ['info', 'pos', 'stats', SOURCE_TABLE_EXT].each do |ext|
407
437
  FileUtils.mv(Dir.glob("#{dump_dir}/*.#{ext}"), dest_dir)
408
438
  end
409
439
  end
@@ -435,29 +465,58 @@ module Flydata
435
465
  end
436
466
  end
437
467
 
438
- def dump_pos_content(status, table_name, last_pos, binlog_pos, state = nil, substate = nil)
439
- [status, table_name, last_pos, binlog_content(binlog_pos), state, substate].join("\t")
468
+ def dump_pos_content(status, table_name, last_pos, source_pos, state = nil, substate = nil)
469
+ [status, table_name, last_pos, source_pos.to_s, state, substate].join("\t")
440
470
  end
441
471
 
442
- def binlog_content(binlog_pos)
443
- binlog_pos.kind_of?(Hash) ?
444
- [binlog_pos[:binfile], binlog_pos[:pos]].join("\t")
445
- : binlog_pos
472
+ def dump_pos_content_to_hash(content)
473
+ items = content.split("\t",-1) # -1 means,(1) there is no limit to the number of fields returned
474
+ # (2) trailing null fields are not suppressed.
475
+ raise "Invalid dump.pos file: #{path}" unless items.length >= 6
476
+
477
+ status = items[0]
478
+ table_name = items[1]
479
+ last_pos = items[2].to_i
480
+
481
+ source_pos_str = items[3..-3].join("\t")
482
+ context = source.source_pos
483
+ source_pos = context.create_source_pos(source_pos_str)
484
+
485
+ state = get_parse_state(items[-2])
486
+ substate = get_parse_state(items[-1])
487
+
488
+ { status: status, table_name: table_name, last_pos: last_pos,
489
+ source_pos: source_pos,
490
+ state: state, substate: substate }
491
+ end
492
+
493
+ def get_parse_state(state)
494
+ state.empty? ? nil : state
446
495
  end
447
496
 
448
- def load_mysql_table_marshal_dump
449
- path = mysql_table_marshal_dump_path
497
+ def load_source_table_marshal_dump
498
+ path = source_table_marshal_dump_path
450
499
  return nil unless File.exists?(path)
451
500
  Marshal.load(File.open(path, 'r'))
452
501
  end
453
502
 
503
+ OLD_DUMP_DIR_PROPERTY = 'mysqldump_dir'
504
+ DUMP_DIR_PROPERTY = 'dump_dir'
454
505
  def dump_dir
455
506
  pref = @data_entry['mysql_data_entry_preference']
456
- dump_dir = if pref and pref['mysqldump_dir']
457
- pref['mysqldump_dir']
458
- else
459
- nil
460
- end
507
+ dump_dir = nil
508
+ dump_dir_property = DUMP_DIR_PROPERTY
509
+
510
+ if pref
511
+ # check the old property for backward compatibility
512
+ [DUMP_DIR_PROPERTY, OLD_DUMP_DIR_PROPERTY].each do |prop|
513
+ if pref[prop]
514
+ dump_dir_property = prop
515
+ dump_dir = pref[prop]
516
+ break
517
+ end
518
+ end
519
+ end
461
520
  if dump_dir
462
521
  dump_dir = dump_dir.dup
463
522
  dump_dir[0] = ENV['HOME'] if dump_dir.match(/^~$|^~\//)
@@ -465,15 +524,15 @@ module Flydata
465
524
  dump_dir = DUMP_DIR.dup
466
525
  end
467
526
  if File.exists?(dump_dir) and not Dir.exists?(dump_dir)
468
- raise "'mysqldump_dir'(#{dump_dir}) must be a directory."
527
+ raise "'#{dump_dir_property}'(#{dump_dir}) must be a directory."
469
528
  end
470
529
  FileUtils.mkdir_p(dump_dir) unless Dir.exists?(dump_dir)
471
530
  dump_dir
472
531
  end
473
532
 
474
- def validate_master_binlog_path(master_binlog_path)
475
- unless master_binlog_path && master_binlog_path.end_with?('binlog.pos')
476
- raise ArgumentError.new("Invalid binlog path. binlog path needs to end with 'binlog.pos'")
533
+ def validate_master_source_pos_path(master_source_pos_path)
534
+ unless master_source_pos_path && master_source_pos_path.end_with?('binlog.pos')
535
+ raise ArgumentError.new("Invalid source position path. Source position path needs to end with 'binlog.pos'")
477
536
  end
478
537
  end
479
538
  end