Sql Server会话上下文限制

Jac*_*cob 5 c# sql-server security dapper

我们在设置和清除会话上下文值时遇到问题.

我们收到以下错误: The value was not set for key X because the total size of keys and values in the session context would exceed the 1 MB limit

我们使用asp.net core和dapper进行数据访问.

打开连接时,我们执行sp_set_session_context并发送4个密钥.3是整数,1是字符串.

在测试中,字符串为null,整数小于10.

执行sql命令后,我们将会话上下文值设置为null,关闭并处置连接.

我们使用以下查询来查看内存使用情况: SELECT SUM([pages_kb]) FROM [sys].[dm_os_memory_cache_counters] WHERE [type] = 'CACHESTORE_SESSION_CONTEXT'

该查询尚未超过1MB.

有谁知道为什么我们收到这个错误?

Jer*_*ert 5

这是一个已知错误,已在 SQL Server 2016 和 2017 中修复。以下脚本将可靠地重现它:

bool unset = true;
using (var command = new SqlCommand()) {
  command.CommandText = "sp_set_session_context";
  command.CommandType = CommandType.StoredProcedure;
  command.Parameters.Add("@key", SqlDbType.NVarChar, 128);
  command.Parameters.Add("@value", SqlDbType.Variant, -1);
  for (int cycles = 0; cycles != 10; ++cycles) { 
    ++cycles;
    using (var connection = 
      new SqlConnection(@"Data Source=(localdb)\MSSqlLocalDB;Integrated Security=SSPI")
    ) {
      connection.Open();
      // Set as many values as we can
      int keys = 0;
      while (true) {
        command.Connection = connection;
        command.Parameters["@key"].Value = keys.ToString();
        command.Parameters["@value"].Value = new String(' ', 8000);
        try {
          command.ExecuteNonQuery();
          ++keys;
        } catch (SqlException e) {
          Console.WriteLine("Failed setting at {0}: {1}", keys, e.Message);
          break;
        }
      }
      if (unset) {
        // Now unset them
        for (; keys >= 0; --keys) {
          command.Connection = connection;
          command.Parameters["@key"].Value = keys.ToString();
          command.Parameters["@value"].Value = DBNull.Value;
          try {
            command.ExecuteNonQuery();
          } catch (SqlException e) {
            Console.WriteLine("Failed unsetting at {0}: {1}", keys, e.Message);
            break;
          }
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

输出:

125 处设置失败:未为键“125”设置值,因为会话上下文中键和值的总大小将超过 1 MB 限制。
120 处设置失败:未为键“120”设置值,因为会话上下文中键和值的总大小将超过 1 MB 限制。
115 处设置失败:未为键“115”设置值,因为会话上下文中键和值的总大小将超过 1 MB 限制。
110 处设置失败:未为键“110”设置值,因为会话上下文中键和值的总大小将超过 1 MB 限制。
105 设置失败:未为键“105”设置值

可用的上下文大小随着每个循环而减少。如果持续足够长的时间,它将下降到某个最小值(不一定是 0)。此时,查询sys.dm_os_memory_cache_counters显示使用的内存远远少于 1 MB,但即便如此,也无法设置更多的会话上下文。

此bug已被固定在SQL Server 2016 SP1 CU8和SQL Server 2017年CU6。(此修复程序提到清理内存问题,如果您使用的是NULL值的关键参数,但使用NULL值的键没有在第一时间允许的,甚至会在旧版本产生错误。根据我的测试中,它一直固定用于取消设置。)

对于以前的版本,至少有两种解决方法:

  • 完成后不要设置这些值NULL,让引擎清除它们。在上面的脚本中,如果设置unsetfalse,您将不会观察到会话上下文泄漏。
  • 使用非NULL标记值来标记“已删除”值,例如空字符串。