Tôi gặp sự cố với điều kiện đua nhibernate trong webapp của tôi.điều kiện đua nhibernate khi tải thực thể
Tôi biết điều này xảy ra khi sử dụng các phiên bản cũ hơn của log4net (nên được sửa trong 1.2.10), mặc dù tôi cũng đã trải nghiệm điều này. Bởi vì điều này chúng tôi đã vô hiệu hóa log4net cho bây giờ, kể từ khi tình trạng cuộc đua sụp đổ IIS và nó không thể chấp nhận cho điều này xảy ra trong sản xuất. Điều này xảy ra khi tải một thực thể (xem stacktrace bên dưới). Bên cạnh đó, một vấn đề tương tự dường như đã xảy ra trong RavenDB, xem này link, và một ví dụ mà không NHibernate ở đây link.
stacktrace:
Server Error in '/' Application.
Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.IndexOutOfRangeException: Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader.
Source Error:
Line 105:
Line 106: if(webUser.Id > 0) { // logged in
Line 107: _user = session.Get<User>(webUser.Id);
Line 108: if(_user == null) { // session exists, but no user in DB with this id
Line 109: new SessionInit().Remove();
Source File: \App_Code\SessionInit.cs Line: 107
Stack Trace:
[IndexOutOfRangeException: Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader.]
System.Buffer.InternalBlockCopy(Array src, Int32 srcOffsetBytes, Array dst, Int32 dstOffsetBytes, Int32 byteCount) +0
System.IO.StreamWriter.Write(Char[] buffer, Int32 index, Int32 count) +117
System.IO.TextWriter.WriteLine(String value) +204
System.IO.SyncTextWriter.WriteLine(String value) +63
NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd) +71
NHibernate.Loader.Loader.GetResultSet(IDbCommand st, Boolean autoDiscoverTypes, Boolean callable, RowSelection selection, ISessionImplementor session) +580
NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +275
NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +205
NHibernate.Loader.Loader.LoadEntity(ISessionImplementor session, Object id, IType identifierType, Object optionalObject, String optionalEntityName, Object optionalIdentifier, IEntityPersister persister) +590
[GenericADOException: could not load an entity: [app.Presentation.User#338][SQL: SELECT user0_.userID as userID24_0_, user0_.instituteID as institut2_24_0_, user0_.email as email24_0_, user0_.password as password24_0_, user0_.username as username24_0_, user0_.mod_remarks as mod6_24_0_, user0_.lastLogin as lastLogin24_0_, user0_.active as active24_0_, user0_.isAcademic as isAcademic24_0_, user0_.created as created24_0_, (select p.firstName from ej_profile p where p.userID = user0_.userID) as formula11_0_, (select p.lastName from ej_profile p where p.userID = user0_.userID) as formula12_0_, (select p.timeZone from ej_profile p where p.userID = user0_.userID) as formula13_0_ FROM ej_user user0_ WHERE user0_.userID=?]]
NHibernate.Loader.Loader.LoadEntity(ISessionImplementor session, Object id, IType identifierType, Object optionalObject, String optionalEntityName, Object optionalIdentifier, IEntityPersister persister) +960
NHibernate.Loader.Entity.AbstractEntityLoader.Load(ISessionImplementor session, Object id, Object optionalObject, Object optionalId) +76
NHibernate.Loader.Entity.AbstractEntityLoader.Load(Object id, Object optionalObject, ISessionImplementor session) +32
NHibernate.Event.Default.DefaultLoadEventListener.LoadFromDatasource(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) +173
NHibernate.Event.Default.DefaultLoadEventListener.Load(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) +181
NHibernate.Event.Default.DefaultLoadEventListener.OnLoad(LoadEvent event, LoadType loadType) +1019
NHibernate.Impl.SessionImpl.FireLoad(LoadEvent event, LoadType loadType) +403
NHibernate.Impl.SessionImpl.Get(String entityName, Object id) +469
NHibernate.Impl.SessionImpl.Get(Type entityClass, Object id) +374
NHibernate.Impl.SessionImpl.Get(Object id) +391
SessionInit.GetCurrentUser(ISession session) in j:\dev\app\app_wwwroot\App_Code\SessionInit.cs:107
DynamicPage.OnPreInit(EventArgs e) in j:\dev\app\app_wwwroot\App_Code\DynamicPage.cs:24
MemberPage.OnPreInit(EventArgs e) in j:\dev\app\app_wwwroot\App_Code\MemberPage.cs:20
members_stocks_Default.OnPreInit(EventArgs e) in j:\dev\app\app_wwwroot\members\Default.aspx.cs:28
System.Web.UI.Page.PerformPreInit() +49
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1716
Việc lập bản đồ cho User:
public class UserViewMapping : ClassMap<User>
{
public UserViewMapping() {
Table("ej_user");
Id(s => s.Id, "userID").GeneratedBy.Native();
Map(s => s.InstituteId, "instituteID");
Map(s => s.Email, "email");
Map(s => s.Password, "password");
Map(s => s.Name, "username");
Map(s => s.ModRemarks, "mod_remarks");
Map(s => s.LastLogin, "lastLogin");
Map(s => s.Active, "active");
Map(s => s.IsAcademic, "isAcademic");
Map(s => s.Created, "created");
Map(s => s.FirstName).Formula("(select p.firstName from ej_profile p where p.userID = userID)");
Map(s => s.LastName).Formula("(select p.lastName from ej_profile p where p.userID = userID)");
Map(s => s.TimeZone).Formula("(select p.timeZone from ej_profile p where p.userID = userID)");
HasMany<ProfileViewModel>(s => s.Profiles)
.Table("ej_profile")
.KeyColumn("userID")
.Cascade.All()
.Inverse();
}
Một số chi tiết: Tôi sử dụng hai phiên cho các truy vấn và các lệnh (và hai nhà máy phiên) kể từ khi tôi sử dụng một phần nào CQRS giống như mẫu. Một phiên để đọc các đối tượng, một để thực hiện các thay đổi (điều này giúp tôi giữ mô hình miền của mình đơn giản và xem các mô hình và ánh xạ có thể khác với mô hình lệnh).
Điều kiện chủng tộc xảy ra khi tải chế độ xem người dùng trong môi trường phát triển của tôi (người dùng đơn), nhưng chúng tôi đảm bảo rằng điều này sẽ không bao giờ xảy ra trong sản xuất, vì nó đã gặp sự cố IIS 7. Ngoài ra, trong sản xuất sẽ có nhiều người dùng, vì vậy có thể lỗi sẽ xảy ra thường xuyên hơn.
Ngoài ra, chúng tôi có nhiều mã kế thừa sử dụng System.Data và MySql.Data.MySqlClient.MySqlDataAdapter để đọc/ghi vào cơ sở dữ liệu. Điều này có thể ảnh hưởng không?
Tôi đang sử dụng NHibernate 3.1.0 (sẽ nâng cấp lên 3.3.1GA, nhưng điều này rất khó để tái tạo) và fluentNhibernate cho ánh xạ của tôi.
Các sessionfactories được tạo ra trong global.asax:
void Application_Start(object sender, EventArgs e)
{
QuerySessionFactory.Create(connectionString);
CommandSessionManager.Initialize(connString);
}
trang của tôi kế thừa từ DynamicPage của tôi, nơi các phiên truy vấn được mở ra, và đóng cửa:
public class DynamicPage : System.Web.UI.Page
{
protected override void OnPreInit(EventArgs e)
{
Session = QuerySessionFactory.Instance.OpenSession();
}
protected override void OnUnload(EventArgs e) {
base.OnUnload(e);
Session.Close();
}
}
Trong SessionInit (đọc userID từ httpcontext.session và tạo 'webuser', một người dùng có một số thông tin đơn giản như userId). Sau đó, tôi đã đặt khóa xung quanh và thực hiện người dùng nhận được yêu cầu trong một giao dịch, không chắc chắn nếu nó sẽ hữu ích.
public IUser GetCurrentUser(ISession session) {
if(_user == null) {
var webUser = new SessionInit().Get;
if(webUser.Id > 0) { // logged in
lock(_lock) {
using(var tx = session.BeginTransaction()) {
_user = session.Get<User>(webUser.Id);
tx.Commit();
}
}
if(_user == null) { // session exists, but no user in DB with this id
new SessionInit().Remove();
}
((User)_user)._currentUser = webUser;
} else {
if(webUser is CurrentUser && webUser.Id == 0) {
if(HttpContext.Current.Session != null) {
HttpContext.Current.Response.Cookies.Remove("ASPSESSID");
HttpContext.Current.Request.Cookies.Remove("ASPSESSID");
HttpContext.Current.Session.RemoveAll();
HttpContext.Current.Session.Abandon();
}
if(HttpContext.Current.Request.Url.Host.Contains("members"))
HttpContext.Current.Response.Redirect("/login");
} else
if(webUser.Id == 0) {
var userId = webUser.Id;
var userName = webUser.UserName;
var loginUrl = webUser.LoginUrl;
var clientIp = webUser.ClientIp;
var isAdmin = webUser.IsAdmin();
return new eLab.Presentation.Visitor(userId, userName, loginUrl, clientIp, isAdmin, webUser.Theme);
}
}
if (_user == null)
return new eLab.Presentation.Visitor(webUser.Id, webUser.UserName, webUser.LoginUrl, webUser.ClientIp, false, webUser.Theme);
}
return _user;
}
Phiên lệnh được mở và đóng trong khối sử dụng khi cần.
Theo stacktrace, sự cố xảy ra trong StreamWriter -> System.Buffer, một lần nữa được gọi bởi System.IO.SyncTextWriter, được cho là trình bao bọc thread-safe xung quanh System.IO.TextWriter.
Vì điều này xảy ra trong TextWriter, có cách nào để giải quyết vấn đề này không, sử dụng TextWriter theo chủ đề không?
Có an toàn để mở và đóng phiên làm cách tôi thực hiện trong DynamicPage không?
Vì điều này rõ ràng là khó để tái tạo, bất kỳ ý tưởng nào về cách thực hiện cũng được chào đón.
[UPDATE] NHibernate Profiler nói rằng chúng tôi cũng đã mở và đóng phiên (trong khối sử dụng) trong trang chính vì cần kiểm tra một số quyền cho người dùng hiện tại, vì vậy hai phiên được mở mỗi yêu cầu. Tôi đã refactored nó, do đó, nó bây giờ thay vì mở một phiên trong một superclass trang, nó sẽ mở phiên trong global.asax trên Application_BeginRequest và đóng lại nó trên Application_EndRequest, nơi phiên được đặt trong HttpContext.Current.Items.
Nhưng không có cách nào chắc chắn để kiểm tra nếu điều này khắc phục.
Tôi quan tâm đến bài viết SO này. Trong ngăn xếp dấu vết, bạn có điều này: - 'SessionInit.GetCurrentUser (phiên ISession) trong j: \ dev \ app \ app_wwwroot \ App_Code \ SessionInit.cs: 107' Có gì trong mã SessionInit.cs? –
Nick, tôi đã thêm mã cho SessionInit.GetCurrentUser(). Xem bản cập nhật. – Stamppot
Tôi nhận được vấn đề này tất cả bây giờ và sau đó quá trong cùng một vị trí chính xác trong NHibernate - bạn đã quản lý để xác định làm thế nào để sửa chữa? –