mysql-net / MySqlConnector

MySQL Connector for .NET
https://mysqlconnector.net
MIT License
1.39k stars 334 forks source link

KeyNotFoundException in MySqlBulkLoader.GetAndRemoveStream() #757

Closed wangjia184 closed 4 years ago

wangjia184 commented 4 years ago

When loading a streaming into a table, MySqlBulkLoader.LoadAsync() may fail with KeyNotFoundException

  at System.ThrowHelper.ThrowKeyNotFoundException[T](T key)
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at MySql.Data.MySqlClient.MySqlBulkLoader.GetAndRemoveStream(String streamKey) in C:\projects\mysqlconnector\src\MySqlConnector\MySql.Data.MySqlClient\MySqlBulkLoader.cs:line 241
   at MySql.Data.MySqlClient.MySqlBulkLoader.<LoadAsync>d__80.MoveNext() in C:\projects\mysqlconnector\src\MySqlConnector\MySql.Data.MySqlClient\MySqlBulkLoader.cs:line 207
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()

My code:

                MySqlBulkLoader loader = new MySqlBulkLoader(conn);
                loader.TableName = "tournament_result";
                loader.FieldTerminator = DataFileGenerator.FIELDS_TERMINATER;
                loader.EscapeCharacter = '\b';
                loader.LineTerminator = DataFileGenerator.LINES_TERMINATER;
                loader.Columns.Add("ID");
                loader.Columns.Add("UserID");
                loader.Columns.Add("Score");
                loader.Columns.Add("Rank");
                loader.Columns.Add("Comments");
                loader.Timeout = 60 * 5;
                loader.Local = true;
                loader.ConflictOption = MySqlBulkLoaderConflictOption.Replace;
                using (loader.SourceStream = generator.GetStream())
                {
                    await loader.LoadAsync().ConfigureAwait(false);
                }

The reason is that, when ExecuteNonQueryAsync fails, closeStream is not set to false but the key does not exist in s_sources.

KeyNotFoundException is raised in the finally block when calling GetAndRemoveSource

https://github.com/mysql-net/MySqlConnector/blob/bec354497d8b26883bf85f07bfdd8abc1222ae55/src/MySqlConnector/MySql.Data.MySqlClient/MySqlBulkLoader.cs#L209

bgrainger commented 4 years ago

The reason is that, when ExecuteNonQueryAsync fails

Your original exception is probably being overwritten by the KeyNotFoundException, but can you explain how or why the original failure is occurring during the bulk load?

wangjia184 commented 4 years ago

The supplied stream contains NULL but quotation is not used hence it cannot be recognized.

Anyway, because the exception is overridden in finally block, it makes harder to locate the issue.

bgrainger commented 4 years ago

Fixed in 0.62.0.