我们正在对大量结构相同的遗留数据库进行不同的集成,这些数据库基本上无法更改。为此,我们添加了一个辅助数据库,用于保存元信息、路由规则以及临时保存遗留数据库的数据。
我们主要使用 NHibernate 来连接数据库。一个应用程序是 WCF 服务,它需要将传入数据插入到非常宽(数十列)的嵌套表中。显然,性能是一个问题,所以我一直在寻找尽可能经济的 NHibernate 交易。同时,并发似乎是一个问题。在生产中,我们开始遇到一些僵化的事务错误(死锁)。
我一直在平衡处理这两个问题,但并没有真正消除并发问题。
服务行为设置为一次处理一个请求,如下所示:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode=ConcurrencyMode.Single]
public class LegacyGateService : ILegacyGateService
早些时候,在互联网的一些“灵感”(阅读:复制/粘贴)之后,我最终添加了一组名为 XxxNHibernateUtil 的类,分别用于辅助数据库和遗留数据库。这些类控制 NHibernate Sessions 并从预初始化的 SessionFactories 生成或重用 Sessions。
对于辅助数据库,它看起来像这样:
public static class LegacyGateNHibernateUtil
{
private static readonly ISessionFactory sessionFactory = BuildSessionFactory();
private static ISessionFactory BuildSessionFactory()
{
try
{
Configuration Cfg = new Configuration();
Cfg.Configure();
Cfg.AddAssembly("LegacyGate.Persistence");
return Cfg.BuildSessionFactory();
}
catch (Exception ex)
{
throw ex;
}
}
public static ISessionFactory GetSessionFactory()
{
return sessionFactory;
}
public static ISession GetCurrentSession()
{
if (!CurrentSessionContext.HasBind(GetSessionFactory()))
CurrentSessionContext.Bind(GetSessionFactory().OpenSession());
return GetSessionFactory().GetCurrentSession();
}
public static void DisposeCurrentSession()
{
ISession currentSession = CurrentSessionContext.Unbind(GetSessionFactory());
if (currentSession != null)
{
if (currentSession.IsOpen)
currentSession.Close();
currentSession.Dispose();
}
}
}
每当事务需要会话时,都会在服务请求调用期间查找并重新使用当前会话。或者至少:这就是应该发生的事情。
编辑:会话上下文当然是在 hibernate.cfg.xml 中设置的,如下所示:
<property name="current_session_context_class">call</property>
对于遗留数据库,NHibernateUtil 适用于处理不同的可能数据库。为此,每个连接都建立了自己的 SessionFactory,必须在 Dictionary 集合中查找。否则,原理相同。
使用 WCFStorm 进行测试,一次发送一个请求时这似乎工作正常,但是一旦我开始负载测试,即使只有一个代理和长间隔,我也会得到大量不同类型的异常,都指向同时请求和相互破坏的交易。我曾尝试调整 IsolationLevel,但现在可用。
我认为我需要以不同的方式生成和处理 Session,以便以有序的方式处理对同一数据库的不同事务,并且不会相互干扰。但是,我对如何完成这项工作缺乏一些见解。任何帮助是极大的赞赏!
编辑对于一种服务方法,当使用多个代理进行测试时,前十几个调用工作正常,然后开始出现以下仅与辅助数据库相关的异常字符串:
- “将 IDataReader 转换为 NDataReader 时出现问题”/“读取器关闭时调用元数据的尝试无效。”
- “非法访问加载集合”
- “开始失败并出现 SQL 异常”/“超时已过期。在操作完成之前超时时间已过或服务器没有响应。”
- “无法执行查询”/“ExecuteReader 需要一个打开且可用的连接。连接的当前状态为关闭。”
- “无法初始化集合:”/“超时。在操作完成之前超时时间已过或服务器没有响应。”
- “交易未成功启动”
- “事务要么与当前连接无关,要么已经完成。”
- “无法初始化集合:”/“读取器关闭时调用读取无效。”
至少,异常 1 表明同一个会话被多个线程访问(可能是调用)。其他的也表示当前会话被其他进程中断。但是,当我试图隔离呼叫并让它们排队时,这怎么可能呢?
对于另一种服务方法,辅助数据库不会出现这些问题,但一段时间后,我开始在遗留数据库的事务中遇到 ZombiedTransaction 异常(死锁)。仍然......什么给了?