Nhibernate 查询反复失败
Nhibernate queries fail itermitently
我很难确定来自 NHibernate (3.3) 的问题。该应用程序在六节点 NLB 集群中为大约 150 req/s 提供服务。该应用程序通常运行正常,但有时可能会在 1-2 天后日志中出现以下错误并且所有查询都失败。
我正在使用 adonet.batch_size
of 15 并且 MultipleActiveResultSets=True
设置为 true。所有控制器都继承自 AsyncController
(这是一个 ASP.NET MVC 4 应用程序),因此会话状态是只读的以最大化并发性。
最初,我们在 NHibernate 会话处理方面遇到了很多麻烦,因为 AbstractBatcher
在管理哪些读者与哪些连接相关方面做得很差。因此,在高负载下,读者会尝试从已经关闭的连接中读取。我们通过手动定义稳定事物的 SQLConnection 的生命周期来解决这个问题。
但是,以下情况经常出现。我认为这是会话工厂确实有问题的征兆。此时,我正在考虑在抛出此类未处理的异常时重新初始化会话工厂,但这并不好。有谁知道为什么会发生这种情况?
System.NotSupportedException: PartialEvaluationExceptionExpression
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitBinaryExpression(BinaryExpression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitBinaryExpression(BinaryExpression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitBinaryExpression(BinaryExpression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
at NHibernate.Linq.Visitors.QueryModelVisitor.VisitWhereClause(WhereClause whereClause, QueryModel queryModel, Int32 index)
at Remotion.Linq.Clauses.WhereClause.Accept(IQueryModelVisitor visitor, QueryModel queryModel, Int32 index)
at Remotion.Linq.QueryModelVisitorBase.VisitBodyClauses(ObservableCollection`1 bodyClauses, QueryModel queryModel)
at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
at NHibernate.Linq.Visitors.QueryModelVisitor.GenerateHqlQuery(QueryModel queryModel, VisitorParameters parameters, Boolean root)
at NHibernate.Linq.NhLinqExpression.Translate(ISessionFactoryImplementor sessionFactory)
at NHibernate.Hql.Ast.ANTLR.ASTQueryTranslatorFactory.CreateQueryTranslators(String queryIdentifier, IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 filters, ISessionFactoryImplementor factory)
at NHibernate.Engine.Query.QueryPlanCache.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters)
at NHibernate.Impl.AbstractSessionImpl.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow)
at NHibernate.Impl.AbstractSessionImpl.CreateQuery(IQueryExpression queryExpression)
at NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query, NhLinqExpression& nhQuery)
at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression)
at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression)
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
对于那些可能面临这个问题的人,我想我会提供一些关于我们为解决这个问题而实施的两年生产(成千上万的用户)的见解。
在基本构造函数中,我们有这样的东西:
public MvcBaseController()
{
try
{
var currentRepositorySession = RepositorySession.Current;
dbConnection = new SqlConnection(currentRepositorySession.GetConnectionString());
dbConnection.Open();
session = currentRepositorySession.Create(false, dbConnection);
}
catch (Exception e)
{
LogDebug("Error was thrown: " + e.Message);
}
}
强制将数据库连接绑定到 NHibernate 会话是确保 AbstractBatcher
不会在所有读取器完成之前关闭会话的唯一方法。在 Dispose
我们有以下内容:
protected override void Dispose(bool disposing)
{
if (disposing)
{
try
{
if (session != null)
{
session.Dispose();
session = null;
}
}
catch (Exception ex)
{
LogDebug("Error was thrown: " + ex.Message);
}
try
{
if (dbConnection != null)
{
dbConnection.Close();
dbConnection.Dispose();
dbConnection = null;
}
}
catch (Exception ex)
{
LogDebug("Error was thrown: " + ex.Message);
}
}
base.Dispose(disposing);
}
这意味着为每个请求打开一个连接,这很好。我们不得不增加SQL个Server worker,但是系统已经稳定了两年了。我们唯一一次看到 NHibernate 会话工厂中断是在从数据库分页的大量数据时,比如 100 万条记录。然后应用程序池溢出,成为孤儿。这些主要是代码错误,与问题中描述的并发问题无关。
我很难确定来自 NHibernate (3.3) 的问题。该应用程序在六节点 NLB 集群中为大约 150 req/s 提供服务。该应用程序通常运行正常,但有时可能会在 1-2 天后日志中出现以下错误并且所有查询都失败。
我正在使用 adonet.batch_size
of 15 并且 MultipleActiveResultSets=True
设置为 true。所有控制器都继承自 AsyncController
(这是一个 ASP.NET MVC 4 应用程序),因此会话状态是只读的以最大化并发性。
最初,我们在 NHibernate 会话处理方面遇到了很多麻烦,因为 AbstractBatcher
在管理哪些读者与哪些连接相关方面做得很差。因此,在高负载下,读者会尝试从已经关闭的连接中读取。我们通过手动定义稳定事物的 SQLConnection 的生命周期来解决这个问题。
但是,以下情况经常出现。我认为这是会话工厂确实有问题的征兆。此时,我正在考虑在抛出此类未处理的异常时重新初始化会话工厂,但这并不好。有谁知道为什么会发生这种情况?
System.NotSupportedException: PartialEvaluationExceptionExpression
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitBinaryExpression(BinaryExpression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitBinaryExpression(BinaryExpression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitBinaryExpression(BinaryExpression expression)
at NHibernate.Linq.Visitors.HqlGeneratorExpressionTreeVisitor.VisitExpression(Expression expression)
at NHibernate.Linq.Visitors.QueryModelVisitor.VisitWhereClause(WhereClause whereClause, QueryModel queryModel, Int32 index)
at Remotion.Linq.Clauses.WhereClause.Accept(IQueryModelVisitor visitor, QueryModel queryModel, Int32 index)
at Remotion.Linq.QueryModelVisitorBase.VisitBodyClauses(ObservableCollection`1 bodyClauses, QueryModel queryModel)
at Remotion.Linq.QueryModelVisitorBase.VisitQueryModel(QueryModel queryModel)
at NHibernate.Linq.Visitors.QueryModelVisitor.GenerateHqlQuery(QueryModel queryModel, VisitorParameters parameters, Boolean root)
at NHibernate.Linq.NhLinqExpression.Translate(ISessionFactoryImplementor sessionFactory)
at NHibernate.Hql.Ast.ANTLR.ASTQueryTranslatorFactory.CreateQueryTranslators(String queryIdentifier, IQueryExpression queryExpression, String collectionRole, Boolean shallow, IDictionary`2 filters, ISessionFactoryImplementor factory)
at NHibernate.Engine.Query.QueryPlanCache.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow, IDictionary`2 enabledFilters)
at NHibernate.Impl.AbstractSessionImpl.GetHQLQueryPlan(IQueryExpression queryExpression, Boolean shallow)
at NHibernate.Impl.AbstractSessionImpl.CreateQuery(IQueryExpression queryExpression)
at NHibernate.Linq.DefaultQueryProvider.PrepareQuery(Expression expression, IQuery& query, NhLinqExpression& nhQuery)
at NHibernate.Linq.DefaultQueryProvider.Execute(Expression expression)
at NHibernate.Linq.DefaultQueryProvider.Execute[TResult](Expression expression)
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
对于那些可能面临这个问题的人,我想我会提供一些关于我们为解决这个问题而实施的两年生产(成千上万的用户)的见解。
在基本构造函数中,我们有这样的东西:
public MvcBaseController()
{
try
{
var currentRepositorySession = RepositorySession.Current;
dbConnection = new SqlConnection(currentRepositorySession.GetConnectionString());
dbConnection.Open();
session = currentRepositorySession.Create(false, dbConnection);
}
catch (Exception e)
{
LogDebug("Error was thrown: " + e.Message);
}
}
强制将数据库连接绑定到 NHibernate 会话是确保 AbstractBatcher
不会在所有读取器完成之前关闭会话的唯一方法。在 Dispose
我们有以下内容:
protected override void Dispose(bool disposing)
{
if (disposing)
{
try
{
if (session != null)
{
session.Dispose();
session = null;
}
}
catch (Exception ex)
{
LogDebug("Error was thrown: " + ex.Message);
}
try
{
if (dbConnection != null)
{
dbConnection.Close();
dbConnection.Dispose();
dbConnection = null;
}
}
catch (Exception ex)
{
LogDebug("Error was thrown: " + ex.Message);
}
}
base.Dispose(disposing);
}
这意味着为每个请求打开一个连接,这很好。我们不得不增加SQL个Server worker,但是系统已经稳定了两年了。我们唯一一次看到 NHibernate 会话工厂中断是在从数据库分页的大量数据时,比如 100 万条记录。然后应用程序池溢出,成为孤儿。这些主要是代码错误,与问题中描述的并发问题无关。