1
1
using System ;
2
+ using System . Buffers . Binary ;
3
+ using System . Collections . Generic ;
4
+ using System . Data ;
2
5
using System . IO ;
3
- using System . Threading . Tasks ;
6
+ using System . Linq ;
4
7
using System . Reflection ;
5
- using System . Buffers . Binary ;
8
+ using System . Threading . Tasks ;
6
9
using Microsoft . Extensions . Logging ;
7
10
using MySql . Data . MySqlClient ;
8
- using SuperSocket . Connection ;
9
- using SuperSocket . Client ;
10
11
using SciSharp . MySQL . Replication . Events ;
12
+ using SuperSocket . Client ;
13
+ using SuperSocket . Connection ;
11
14
12
15
namespace SciSharp . MySQL . Replication
13
16
{
@@ -40,6 +43,8 @@ public class ReplicationClient : EasyClient<LogEvent>, IReplicationClient
40
43
set { base . Logger = value ; }
41
44
}
42
45
46
+ private readonly Dictionary < string , TableSchema > _tableSchemaMap ;
47
+
43
48
static ReplicationClient ( )
44
49
{
45
50
LogEventPackageDecoder . RegisterEmptyPayloadEventTypes (
@@ -68,11 +73,17 @@ static ReplicationClient()
68
73
/// Initializes a new instance of the <see cref="ReplicationClient"/> class.
69
74
/// </summary>
70
75
public ReplicationClient ( )
71
- : base ( new LogEventPipelineFilter ( ) )
76
+ : this ( new LogEventPipelineFilter ( ) )
72
77
{
73
78
74
79
}
75
80
81
+ private ReplicationClient ( LogEventPipelineFilter logEventPipelineFilter )
82
+ : base ( logEventPipelineFilter )
83
+ {
84
+ _tableSchemaMap = ( logEventPipelineFilter . Context as ReplicationState ) . TableSchemaMap ;
85
+ }
86
+
76
87
/// <summary>
77
88
/// Gets the underlying stream from a MySQL connection.
78
89
/// </summary>
@@ -104,7 +115,7 @@ public async Task<LoginResult> ConnectAsync(string server, string username, stri
104
115
105
116
try
106
117
{
107
- await mysqlConn . OpenAsync ( ) ;
118
+ await mysqlConn . OpenAsync ( ) . ConfigureAwait ( false ) ;
108
119
}
109
120
catch ( Exception e )
110
121
{
@@ -117,16 +128,18 @@ public async Task<LoginResult> ConnectAsync(string server, string username, stri
117
128
118
129
try
119
130
{
120
- var binlogInfo = await GetBinlogFileNameAndPosition ( mysqlConn ) ;
131
+ await LoadDatabaseSchemaAsync ( mysqlConn ) . ConfigureAwait ( false ) ;
132
+
133
+ var binlogInfo = await GetBinlogFileNameAndPosition ( mysqlConn ) . ConfigureAwait ( false ) ;
121
134
122
- var binlogChecksum = await GetBinlogChecksum ( mysqlConn ) ;
123
- await ConfirmChecksum ( mysqlConn ) ;
135
+ var binlogChecksum = await GetBinlogChecksum ( mysqlConn ) . ConfigureAwait ( false ) ;
136
+ await ConfirmChecksum ( mysqlConn ) . ConfigureAwait ( false ) ;
124
137
LogEvent . ChecksumType = binlogChecksum ;
125
138
126
139
_stream = GetStreamFromMySQLConnection ( mysqlConn ) ;
127
140
_serverId = serverId ;
128
141
129
- await StartDumpBinlog ( _stream , serverId , binlogInfo . Item1 , binlogInfo . Item2 ) ;
142
+ await StartDumpBinlog ( _stream , serverId , binlogInfo . Item1 , binlogInfo . Item2 ) . ConfigureAwait ( false ) ;
130
143
131
144
_connection = mysqlConn ;
132
145
@@ -143,7 +156,7 @@ public async Task<LoginResult> ConnectAsync(string server, string username, stri
143
156
}
144
157
catch ( Exception e )
145
158
{
146
- await mysqlConn . CloseAsync ( ) ;
159
+ await mysqlConn . CloseAsync ( ) . ConfigureAwait ( false ) ;
147
160
148
161
return new LoginResult
149
162
{
@@ -153,6 +166,50 @@ public async Task<LoginResult> ConnectAsync(string server, string username, stri
153
166
}
154
167
}
155
168
169
+ private async Task LoadDatabaseSchemaAsync ( MySqlConnection mysqlConn )
170
+ {
171
+ var tableSchemaTable = await mysqlConn . GetSchemaAsync ( "Columns" ) . ConfigureAwait ( false ) ;
172
+
173
+ var systemDatabases = new HashSet < string > (
174
+ new [ ] { "mysql" , "information_schema" , "performance_schema" , "sys" } ,
175
+ StringComparer . OrdinalIgnoreCase ) ;
176
+
177
+ var userDatabaseColumns = tableSchemaTable . Rows . OfType < DataRow > ( )
178
+ . Where ( row => ! systemDatabases . Contains ( row . ItemArray [ 1 ] . ToString ( ) ) )
179
+ . ToArray ( ) ;
180
+
181
+ userDatabaseColumns . Select ( row =>
182
+ {
183
+ var columnSizeCell = row [ "CHARACTER_MAXIMUM_LENGTH" ] ;
184
+
185
+ return new {
186
+ TableName = row [ "TABLE_NAME" ] . ToString ( ) ,
187
+ DatabaseName = row [ "TABLE_SCHEMA" ] . ToString ( ) ,
188
+ ColumnName = row [ "COLUMN_NAME" ] . ToString ( ) ,
189
+ ColumnType = row [ "DATA_TYPE" ] . ToString ( ) ,
190
+ ColumnSize = columnSizeCell == DBNull . Value ? 0 : Convert . ToUInt64 ( columnSizeCell ) ,
191
+ } ;
192
+ } )
193
+ . GroupBy ( row => new { row . TableName , row . DatabaseName } )
194
+ . ToList ( )
195
+ . ForEach ( group =>
196
+ {
197
+ var tableSchema = new TableSchema
198
+ {
199
+ TableName = group . Key . TableName ,
200
+ DatabaseName = group . Key . DatabaseName ,
201
+ Columns = group . Select ( row => new ColumnSchema
202
+ {
203
+ Name = row . ColumnName ,
204
+ DataType = row . ColumnType ,
205
+ ColumnSize = row . ColumnSize
206
+ } ) . ToList ( )
207
+ } ;
208
+
209
+ _tableSchemaMap [ $ "{ group . Key . DatabaseName } .{ group . Key . TableName } "] = tableSchema ;
210
+ } ) ;
211
+ }
212
+
156
213
/// <summary>
157
214
/// Retrieves the binary log file name and position from the MySQL server.
158
215
/// https://dev.mysql.com/doc/refman/5.6/en/replication-howto-masterstatus.html
@@ -164,15 +221,15 @@ private async Task<Tuple<string, int>> GetBinlogFileNameAndPosition(MySqlConnect
164
221
var cmd = mysqlConn . CreateCommand ( ) ;
165
222
cmd . CommandText = "SHOW MASTER STATUS;" ;
166
223
167
- using ( var reader = await cmd . ExecuteReaderAsync ( ) )
224
+ using ( var reader = await cmd . ExecuteReaderAsync ( ) . ConfigureAwait ( false ) )
168
225
{
169
226
if ( ! await reader . ReadAsync ( ) )
170
227
throw new Exception ( "No binlog information has been returned." ) ;
171
228
172
229
var fileName = reader . GetString ( 0 ) ;
173
230
var position = reader . GetInt32 ( 1 ) ;
174
231
175
- await reader . CloseAsync ( ) ;
232
+ await reader . CloseAsync ( ) . ConfigureAwait ( false ) ;
176
233
177
234
return new Tuple < string , int > ( fileName , position ) ;
178
235
}
@@ -190,11 +247,11 @@ private async Task<ChecksumType> GetBinlogChecksum(MySqlConnection mysqlConn)
190
247
191
248
using ( var reader = await cmd . ExecuteReaderAsync ( ) )
192
249
{
193
- if ( ! await reader . ReadAsync ( ) )
250
+ if ( ! await reader . ReadAsync ( ) . ConfigureAwait ( false ) )
194
251
return ChecksumType . NONE ;
195
252
196
253
var checksumTypeName = reader . GetString ( 1 ) . ToUpper ( ) ;
197
- await reader . CloseAsync ( ) ;
254
+ await reader . CloseAsync ( ) . ConfigureAwait ( false ) ;
198
255
199
256
return ( ChecksumType ) Enum . Parse ( typeof ( ChecksumType ) , checksumTypeName ) ;
200
257
}
@@ -209,7 +266,7 @@ private async ValueTask ConfirmChecksum(MySqlConnection mysqlConn)
209
266
{
210
267
var cmd = mysqlConn . CreateCommand ( ) ;
211
268
cmd . CommandText = "set @`master_binlog_checksum` = @@binlog_checksum;" ;
212
- await cmd . ExecuteNonQueryAsync ( ) ;
269
+ await cmd . ExecuteNonQueryAsync ( ) . ConfigureAwait ( false ) ;
213
270
}
214
271
215
272
/// <summary>
@@ -269,8 +326,8 @@ private Memory<byte> GetDumpBinlogCommand(int serverId, string fileName, int pos
269
326
private async ValueTask StartDumpBinlog ( Stream stream , int serverId , string fileName , int position )
270
327
{
271
328
var data = GetDumpBinlogCommand ( serverId , fileName , position ) ;
272
- await stream . WriteAsync ( data ) ;
273
- await stream . FlushAsync ( ) ;
329
+ await stream . WriteAsync ( data ) . ConfigureAwait ( false ) ;
330
+ await stream . FlushAsync ( ) . ConfigureAwait ( false ) ;
274
331
}
275
332
276
333
/// <summary>
@@ -307,10 +364,10 @@ public override async ValueTask CloseAsync()
307
364
if ( connection != null )
308
365
{
309
366
_connection = null ;
310
- await connection . CloseAsync ( ) ;
367
+ await connection . CloseAsync ( ) . ConfigureAwait ( false ) ;
311
368
}
312
369
313
- await base . CloseAsync ( ) ;
370
+ await base . CloseAsync ( ) . ConfigureAwait ( false ) ;
314
371
}
315
372
}
316
373
}
0 commit comments