AlwaysEncrypted Вставить сбои периодически
Мы используем функцию AlwaysEncrypted Microsoft SQL уже более года. До сих пор это работало без нареканий. Ранее у нас было только несколько зашифрованных столбцов (4). Неделю назад мы внедрили шифрование еще в 11 столбцах, поэтому все еще используем его очень легко.
К сожалению, некоторые из наших команд "INSERT" для таблиц с зашифрованными столбцами не работают, но только с перерывами. Команды выполняются веб-сервером (сервером IIS). Я могу выполнить команду, если она завершится с ошибкой, и сразу же после того, как будет сгенерировано исключение, выполнить ту же команду в том же процессе (веб-сервер просто обработал исключение), и оно выполнится успешно.
Двоичный файл сервера написан на C#, а команды абсолютно тривиальны. Например, выполнение этой команды завершается неудачно:
INSERT INTO [dbo].[someTable] ( [COL1], [COL2], [COL3] ) VALUES ( @COL1_0, @COL2_0, @COL3_0 )
где все три столбца (COL1, COL2 и COL3) зашифрованы, и [someTable] имеет только эти три столбца, все varchar(64), плюс столбец целочисленных идентификаторов.
Код C# (перефразированный) это:
SqlCommand cmd = new SqlCommand();
cmd.Connection = GetOpenConnection("Data Source=REDACTED;Integrated Security=SSPI;Column Encryption Setting=enabled;Initial Catalog=PRODUCTION");
cmd.CommandText = "INSERT INTO [dbo].[someTable] ( [COL1], [COL2], [COL3] ) VALUES ( @COL1_0, @COL2_0, @COL3_0 )";
cmd.CommandType = CommandType.Text;
cmd.Transaction = m_Transaction;
foreach(string[] paramData in inputParamsData) //3 of these
{
//@COLx_0
SqlParameter param = new SqlParameter(paramData[0], SqlDbType.VarChar, 64);
param.Value = paramData[1];
cmd.Parameters.Add(param);
}
string msg = "Preparing";
try
{
cmd.Prepare();
msg = "Executing";
return cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
if (ex.Message.Contains("Operand type clash"))
{
log.Error($"{msg} non-query", ex);
log.Error("Parameters in command");
foreach (var obj in command.Parameters)
{
SqlParameter param = obj as SqlParameter;
if (param != null)
{
log.ErrorFormat("Param name: {0}, length: {1}", param.ParameterName, param.Size);
}
}
}
throw;
}
Выполнение не выполняется ExecuteNonQuery()
(хотя в тексте сообщения об исключении говорится, что он не может "подготовить" запрос, проверка сообщений регистрации показывает, что Prepare
успешно и ExecuteNonQuery
не удается.) Регистрация мы получаем:
2019-02-21 23:15:43,046 ERROR [15:xxx.DbConnectionWrapper`6] - Executing non-query
System.Data.SqlClient.SqlException (0x80131904): Operand type clash: varchar is incompatible with varchar(64) encrypted with (encryption_type = 'DETERMINISTIC', encryption_algorithm_name = 'AEAD_AES_256_CBC_HMAC_SHA_256', column_encryption_key_name = 'ColumnKey_20180605_123456', column_encryption_key_database_name = 'Production') collation_name = 'Latin1_General_BIN2'
Statement(s) could not be prepared.
...
Error Number:206,State:2,Class:16
2019-02-21 23:15:43,067 ERROR [15:xxx.DbConnectionWrapper`6] - Parameters in command
2019-02-21 23:15:43,069 ERROR [15:xxx.DbConnectionWrapper`6] - Param name: @FIGI_0, length: 64
Видно, что размер параметра действительно установлен правильно. Единственный столбец, который зашифрован, это столбец "FIGI", который является столбцом varchar(64), и этот параметр, по-видимому, задан правильно.
У нас есть Microsoft SQL Server Enterprise (64-разрядная версия), версия 13.0.5026.0. Сервер IIS имеет.NET 4.6.1.
Кто-нибудь испытывал такие периодические сбои? У кого-нибудь есть указания, где искать проблему?
Отмечу что класс SqlParameter
имеет свойство ForceColumnEncryption
, но очень мало документации, чтобы указать, когда, где и как этот параметр следует использовать. Кто-нибудь может объяснить его использование или указать мне на достойную документацию?
Редактировать: оказывается, прерывистый характер хуже, чем этот. Это терпит неудачу только приблизительно один раз в неделю. Приведенные ниже комментарии относительно размера параметров, я добавил в некоторые журналы отладки, когда это не удается, с результатами, как указано выше.