gpt4 book ai didi

c# - ServiceStack/FluentNHibernate/MySQL - 两个并发请求使用相同的连接

转载 作者:行者123 更新时间:2023-11-29 01:41:43 25 4
gpt4 key购买 nike

我们似乎遇到了一个奇怪的问题,即对我们服务的两个并发请求实际上使用了同一个数据库连接。

我们的设置是 ServiceStack + NHibernate + FluentNHibernate + MySQL。我已经设置了一个小测试来重现问题:

public class AppHost : AppHostBase
{
private ISessionFactory _sessionFactory;

public AppHost() : base("Lala Service", typeof(AppHost).Assembly)
{
}

public override void Configure(Container container)
{
_sessionFactory = Fluently.Configure()

.Database(MySQLConfiguration.Standard.ConnectionString(conn =>
conn.Server("localhost").Username("lala").Password("lala").Database("lala")))

.Mappings(mappings => mappings.AutoMappings.Add(
AutoMap.Assembly(GetType().Assembly).Where(t => t == typeof(Lala))
.Conventions.Add(DefaultLazy.Never(), DefaultCascade.All())))

.BuildSessionFactory();

container.Register(c => _sessionFactory.OpenSession()).ReusedWithin(ReuseScope.Request);
}
}

public class Lala
{
public int ID { get; set; }
public string Name { get; set; }
}

[Route("/lala")]
public class LalaRequest
{
}

public class LalaReseponse
{
}

public class LalaService : Service
{
private ISession _session;

public ISession Session1
{
get { return _session; }
set { _session = value; }
}

public LalaReseponse Get(LalaRequest request)
{
var lala = new Lala
{
Name = Guid.NewGuid().ToString()
};

_session.Persist(lala);
_session.Flush();

lala.Name += " XXX";

_session.Flush();

return new LalaReseponse();
}
}

我像这样通过 Ajax 同时点击此服务 10 次:

    <script type="text/javascript">
for (i = 0; i < 10; i++) {
console.log("aa");
$.ajax({
url: '/lala',
dataType: 'json',
cache: false
});
}
</script>

结果始终是:

  1. 打开的连接数 < 10。
  2. 并非所有记录都已更新。
  3. 有时 - 如果我删除记录,则会抛出 StaleObjectStateException

这背后的原因是连接被两个并发请求重用,然后 LAST_INSERT_ID() 给出了错误行的 ID,因此两个请求正在更新同一行。

简而言之:它完全是一团糟,而且它显然在请求之间共享数据库连接。

问题是:为什么?我应该如何配置才能让每个请求从连接池获得自己的连接?

最佳答案

终于解决了,真是浪费一天时间!

问题的根源是NHibernate的connection release mode :

11.7. Connection Release Modes

The legacy (1.0.x) behavior of NHibernate in regards to ADO.NET connection management was that a ISession would obtain a connection when it was first needed and then hold unto that connection until the session was closed. NHibernate introduced the notion of connection release modes to tell a session how to handle its ADO.NET connections. ... The different release modes are identified by the enumerated values of NHibernate.ConnectionReleaseMode:

  • OnClose - is essentially the legacy behavior described above. The NHibernate session obtains a connection when it first needs to perform some database access and holds unto that connection until the session is closed.

  • AfterTransaction - says to release connections after a NHibernate.ITransaction has completed.

The configuration parameter hibernate.connection.release_mode is used to specify which release mode to use.

...

  • after_transaction - says to use ConnectionReleaseMode.AfterTransaction. Note that with ConnectionReleaseMode.AfterTransaction, if a session is considered to be in auto-commit mode (i.e. no transaction was started) connections will be released after every operation.

这与 MySQL .NET/Connector 的默认连接池纠缠在一起,实际上意味着连接在并发请求之间交换,因为一个请求将连接释放回池中,而另一个请求获取它。

但是,我认为 NHibernate 在释放并重新获取连接后调用 LAST_INSERT_ID() 这一事实是一个错误。它应该在同一个“操作”中调用 LAST_INSERT_ID()

无论如何,解决方案:

  1. 使用交易,这是我们通常做的,或者
  2. 如果您出于某种原因不能或不想在特定上下文中使用事务(这正是今天发生的情况),请将连接 Release模式设置为“关闭时”。使用 FluentNHibernate 将是:

    .ExposeConfiguration(cfg =>
    cfg.SetProperty("connection.release_mode", "on_close"));

    从这里开始,即使没有事务,连接也会绑定(bind)到 session 。

关于c# - ServiceStack/FluentNHibernate/MySQL - 两个并发请求使用相同的连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19379739/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com