2014-10-23 12 views
5

Tôi có một ứng dụng sản xuất (IIS8, MVC5, nHibernate DAL) và tôi nhận thấy mức sử dụng CPU cao vào cuối năm. Đi xe đạp hồ bơi ứng dụng sửa chữa nó nhưng sau khi thực hiện một số chẩn đoán và bãi bộ nhớ từ máy chủ để phân tích vấn đề, tôi nhận thấy một mẫu nhất quán của nhiều chủ đề cố gắng liệt kê cùng một bộ sưu tập. Điểm phổ biến nhất là nơi ứng dụng kiểm tra vai trò của người dùng. Tôi nghi ngờ điều này có thể được nhiều hơn để thực tế là mã này được chạy cho mọi yêu cầu để xác minh quyền, do đó, nó có nhiều khả năng là bộ sưu tập nó bị mắc kẹt trên?nHibernate liệt kê cùng một bộ sưu tập trên nhiều chủ đề

public IList<Role> GetRoles(string username) 
{ 
    var login = GetLoginForUser(username); 
    return !login.Groups.Any() ? new List<Role>() : login.Groups.SelectMany(x => x.Roles).OrderBy(x => x.DisplayName).ToList(); 
} 

Đối tượng CurrentUser của tôi có giao diện đơn giản chứa thông tin chi tiết của người dùng, được chèn từ trình phân giải phụ thuộc. Tôi đã xác minh rằng UserId hiện diện và hợp lệ, tất cả đều khá thẳng về phía trước. Khi tôi xem xét các bãi khi hai yêu cầu này được treo, tôi nhận được cảnh báo rằng nhiều luồng đã liệt kê một bộ sưu tập. Khi tôi kiểm tra hai luồng trong bãi chứa, tôi thấy các dấu vết ngăn xếp giống hệt nhau. (Tôi đã đổi tên một số chi tiết không gian tên trong ngăn xếp ngăn xếp nhưng nó không bị thay đổi). Các userId (và kết quả hồ sơ) trong cả hai yêu cầu là như nhau, do đó, nó xuất hiện đó là do hai chủ đề riêng biệt cố gắng để tải cùng một đối tượng từ cơ sở dữ liệu tại thực tế cùng một lúc.

Dấu vết ngăn xếp bên dưới, nhưng tôi không chắc chắn nên đi đâu từ đây để sửa lỗi này.

System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.Nullable`1[[System.Int32, mscorlib]], mscorlib]].FindEntry(System.__Canon)+129 
System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.Nullable`1[[System.Int32, mscorlib]], mscorlib]].TryGetValue(System.__Canon, System.Nullable`1<Int32> ByRef)+12 
NHibernate.AdoNet.ColumnNameCache.GetIndexForColumnName(System.String, NHibernate.AdoNet.ResultSetWrapper)+25 
NHibernate.AdoNet.ColumnNameCache.GetIndexForColumnName(System.String, NHibernate.AdoNet.ResultSetWrapper)+25 
NHibernate.AdoNet.ResultSetWrapper.GetOrdinal(System.String)+e 
NHibernate.AdoNet.ResultSetWrapper.GetOrdinal(System.String)+e 
NHibernate.Type.NullableType.NullSafeGet(System.Data.IDataReader, System.String)+29 
NHibernate.Type.NullableType.NullSafeGet(System.Data.IDataReader, System.String[], NHibernate.Engine.ISessionImplementor, System.Object)+16 
NHibernate.Type.NullableType.NullSafeGet(System.Data.IDataReader, System.String[], NHibernate.Engine.ISessionImplementor, System.Object)+16 
NHibernate.Persister.Collection.AbstractCollectionPersister.ReadKey(System.Data.IDataReader, System.String[], NHibernate.Engine.ISessionImplementor)+14 
NHibernate.Persister.Collection.AbstractCollectionPersister.ReadKey(System.Data.IDataReader, System.String[], NHibernate.Engine.ISessionImplementor)+14 
NHibernate.Loader.Loader.ReadCollectionElement(System.Object, System.Object, NHibernate.Persister.Collection.ICollectionPersister, NHibernate.Loader.ICollectionAliases, System.Data.IDataReader, NHibernate.Engine.ISessionImplementor)+34 
NHibernate.Loader.Loader.ReadCollectionElement(System.Object, System.Object, NHibernate.Persister.Collection.ICollectionPersister, NHibernate.Loader.ICollectionAliases, System.Data.IDataReader, NHibernate.Engine.ISessionImplementor)+34 
NHibernate.Loader.Loader.ReadCollectionElements(System.Object[], System.Data.IDataReader, NHibernate.Engine.ISessionImplementor)+d2 
NHibernate.Loader.Loader.ReadCollectionElements(System.Object[], System.Data.IDataReader, NHibernate.Engine.ISessionImplementor)+d2 
NHibernate.Loader.Loader.GetRowFromResultSet(System.Data.IDataReader, NHibernate.Engine.ISessionImplementor, NHibernate.Engine.QueryParameters, NHibernate.LockMode[], NHibernate.Engine.EntityKey, System.Collections.IList, NHibernate.Engine.EntityKey[], Bo+ab 
NHibernate.Loader.Loader.GetRowFromResultSet(System.Data.IDataReader, NHibernate.Engine.ISessionImplementor, NHibernate.Engine.QueryParameters, NHibernate.LockMode[], NHibernate.Engine.EntityKey, System.Collections.IList, NHibernate.Engine.EntityKey[], Bo+ab 
NHibernate.Loader.Loader.DoQuery(NHibernate.Engine.ISessionImplementor, NHibernate.Engine.QueryParameters, Boolean)+1e7 
NHibernate.Loader.Loader.DoQuery(NHibernate.Engine.ISessionImplementor, NHibernate.Engine.QueryParameters, Boolean)+1e7 
NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(NHibernate.Engine.ISessionImplementor, NHibernate.Engine.QueryParameters, Boolean)+7f 
NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(NHibernate.Engine.ISessionImplementor, NHibernate.Engine.QueryParameters, Boolean)+7f 
NHibernate.Loader.Loader.LoadCollection(NHibernate.Engine.ISessionImplementor, System.Object, NHibernate.Type.IType)+de 
NHibernate.Loader.Loader.LoadCollection(NHibernate.Engine.ISessionImplementor, System.Object, NHibernate.Type.IType)+de 
NHibernate.Loader.Collection.CollectionLoader.Initialize(System.Object, NHibernate.Engine.ISessionImplementor)+1c 
NHibernate.Loader.Collection.CollectionLoader.Initialize(System.Object, NHibernate.Engine.ISessionImplementor)+1c 
NHibernate.Persister.Collection.AbstractCollectionPersister.Initialize(System.Object, NHibernate.Engine.ISessionImplementor)+1e 
NHibernate.Persister.Collection.AbstractCollectionPersister.Initialize(System.Object, NHibernate.Engine.ISessionImplementor)+1e 
NHibernate.Event.Default.DefaultInitializeCollectionEventListener.OnInitializeCollection(NHibernate.Event.InitializeCollectionEvent)+16d 
NHibernate.Impl.SessionImpl.InitializeCollection(NHibernate.Collection.IPersistentCollection, Boolean)+1fa 
NHibernate.Collection.AbstractPersistentCollection.Initialize(Boolean)+2f 
NHibernate.Collection.AbstractPersistentCollection.Read()+d 
NHibernate.Collection.Generic.PersistentGenericBag`1[[System.__Canon, mscorlib]].System.Collections.Generic.IEnumerable<T>.GetEnumerator()+11 
System_Core_ni!System.Linq.Enumerable+<SelectManyIterator>d__14`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]].MoveNext()+10c 
System_Core_ni!System.Linq.Buffer`1[[System.__Canon, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<System.__Canon>)+d9 
System_Core_ni!System.Linq.OrderedEnumerable`1+<GetEnumerator>d__0[[System.__Canon, mscorlib]].MoveNext()+6f 
System_Core_ni!System.Linq.OrderedEnumerable`1+<GetEnumerator>d__0[[System.__Canon, mscorlib]].MoveNext()+6f 
mscorlib_ni!System.Collections.Generic.List`1[[System.__Canon, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<System.__Canon>)+17e 
System_Core_ni!System.Linq.Enumerable.ToList[[System.__Canon, mscorlib]](System.Collections.Generic.IEnumerable`1<System.__Canon>)+3b 
Company.ApplicationServices.SecurityService.GetRoles(System.String)+ef 

Tôi hiện đang mở giao dịch cơ sở dữ liệu của tôi trong một ActionFilter mở giao dịch khi OnActionExecuting() xảy ra, và sau đó cam kết/rollback giao dịch khi OnActionExecuted() xảy ra.

Tôi đang sử dụng StructureMap (v2.6.4.1) để tiêm phụ thuộc của tôi và các dòng có liên quan để duy trì dữ liệu của tôi như sau.

var cfg = Fluently.Configure() 
    .Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("DatabaseConnectionString")) 
    .CurrentSessionContext<WebSessionContext>() 
    // ... etc etc.... 
    .Cache(c => c.ProviderClass<NHibernate.Caches.SysCache2.SysCacheProvider>() 
       .UseQueryCache() 
       .UseSecondLevelCache() 
       .UseMinimalPuts()); 

For<NHibernate.Cfg.Configuration>().Singleton().Use(cfg); 
For<NHibernate.ISessionFactory>().Singleton() 
    .Use(ctx => 
     { 
      try 
      { 
       var config = ctx.GetInstance<NHibernate.Cfg.Configuration>(); 
       return config.BuildSessionFactory(); 
      } 
      catch (SqlException ex) 
      { 
       ctx.GetInstance<IExceptionLogger>().Error(ex); 
       throw; 
      } 
     }); 
For<NHibernate.ISession>().HybridHttpOrThreadLocalScoped() 
    .Use(ctx => ctx.GetInstance<NHibernate.ISessionFactory>().OpenSession()); 

UPDATE: Tôi vẫn đang đối phó với điều này và rất thích một số lời khuyên về nếu điều này là một vấn đề với nhibernate, hoặc làm thế nào tôi có nó được cấu hình? Tôi đã có các khóa ứng dụng đến điểm mà chúng tôi đã phải khởi động lại máy chủ ngày hôm nay vì 19 chủ đề riêng biệt cố gắng liệt kê cùng một bộ sưu tập.

Nó được đề cập dưới đây có thể là một vấn đề với phạm vi cuộc sống của SecurityService, mà tôi đồng ý là một khả năng. Hiện tại tôi có các dịch vụ được cung cấp thông qua tiêm phụ thuộc thông qua Structuremap (phiên bản cuối cùng của phiên bản 2.6 được phát hành, chưa cập nhật lên 3.x). Chi tiết trong đó tôi đã nêu chi tiết một thời gian ngắn dưới đây cho những gì tôi hy vọng là ngắn gọn nhưng vẫn có liên quan.

public class SecurityService : ISecurityService 
{ 
    private readonly IRepository<Login> loginRepository; 

    public IList<Role> GetCurrentUserRoles() 
    { 
     var login = GetLoginForCurrentUser(); 
     return GetRoles(login.Name); 
    } 

    public Login GetLoginForCurrentUser() 
    { 
     //Some logic to derive the current UserId {guid} via some resources injected into this service class. 

     return loginRepository.GetReference(loginId); 
    } 
} 

public class NHibernateRepository<T> : IRepository<T> where T : class 
{ 
    protected ISession Session { get; set; } 

    public NHibernateRepository(ISession session) 
    { 
     Session = session; 
    } 

    public T GetReference(object id) 
    { 
     return Session.Get<T>(id); 
    } 

    // Other methods typical of a repository class, nothing special 
} 

tôi thiết lập phụ thuộc resolver ....

For<ISecurityService>().Use<SecurityService>(); 
For(typeof (IRepository<>)).Use(typeof (NHibernateRepository<>)); 
//And then the ISession is commented above. 

nHibernate được cấu hình với một bối cảnh nội bộ của WebSessionContext ISessionFactory là Singleton ISession là HybridHttpOrThreadLocalScoped ISecurityService và IRepository đều cả trái t mặc định của Transient

Các vai trò được lưu trong bộ nhớ cache và nếu không được tìm thấy thì hệ thống sẽ thực hiện cuộc gọi đến GetRoles cho tôi trên dịch vụ bảo mật, tôi nghĩ rằng tôi có thể có một vấn đề với nó gọi GetRoles thường xuyên hơn nó cần, nhưng đó là bên ngoài phạm vi của nhiều vấn đề liệt kê đồng thời tôi đang có bây giờ.

CẬP NHẬT: Vì vậy, tôi gặp khó khăn, hôm nay tôi nhận được cùng một sự cố cho cuộc gọi đến GetReference. 18 chủ đề riêng biệt bị mắc kẹt liệt kê cùng một bộ sưu tập, nhưng điều này là nội bộ để nHibernate.

System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.Nullable`1[[System.Int32, mscorlib]], mscorlib]].FindEntry(System.__Canon)+129 
System.Collections.Generic.Dictionary`2[[System.__Canon, mscorlib],[System.Nullable`1[[System.Int32, mscorlib]], mscorlib]].TryGetValue(System.__Canon, System.Nullable`1 ByRef)+12 
NHibernate.AdoNet.ColumnNameCache.GetIndexForColumnName(System.String, NHibernate.AdoNet.ResultSetWrapper)+25 
NHibernate.AdoNet.ResultSetWrapper.GetOrdinal(System.String)+e 
NHibernate.Type.NullableType.NullSafeGet(System.Data.IDataReader, System.String)+29 
NHibernate.Type.NullableType.NullSafeGet(System.Data.IDataReader, System.String[], NHibernate.Engine.ISessionImplementor, System.Object)+16 
NHibernate.Type.AbstractType.Hydrate(System.Data.IDataReader, System.String[], NHibernate.Engine.ISessionImplementor, System.Object)+14 
NHibernate.Persister.Entity.AbstractEntityPersister.Hydrate(System.Data.IDataReader, System.Object, System.Object, NHibernate.Persister.Entity.ILoadable, System.String[][], Boolean, NHibernate.Engine.ISessionImplementor)+3ce 
NHibernate.Loader.Loader.LoadFromResultSet(System.Data.IDataReader, Int32, System.Object, System.String, NHibernate.Engine.EntityKey, System.String, NHibernate.LockMode, NHibernate.Persister.Entity.ILoadable, NHibernate.Engine.ISessionImplementor)+118 
NHibernate.Loader.Loader.InstanceNotYetLoaded(System.Data.IDataReader, Int32, NHibernate.Persister.Entity.ILoadable, NHibernate.Engine.EntityKey, NHibernate.LockMode, System.String, NHibernate.Engine.EntityKey, System.Object, System.Collections.IList, NHi+8c 
NHibernate.Loader.Loader.GetRow(System.Data.IDataReader, NHibernate.Persister.Entity.ILoadable[], NHibernate.Engine.EntityKey[], System.Object, NHibernate.Engine.EntityKey, NHibernate.LockMode[], System.Collections.IList, NHibernate.Engine.ISessionImpleme+129 
NHibernate.Loader.Loader.GetRowFromResultSet(System.Data.IDataReader, NHibernate.Engine.ISessionImplementor, NHibernate.Engine.QueryParameters, NHibernate.LockMode[], NHibernate.Engine.EntityKey, System.Collections.IList, NHibernate.Engine.EntityKey[], Bo+97 
NHibernate.Loader.Loader.DoQuery(NHibernate.Engine.ISessionImplementor, NHibernate.Engine.QueryParameters, Boolean)+1e7 
NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(NHibernate.Engine.ISessionImplementor, NHibernate.Engine.QueryParameters, Boolean)+7f 
NHibernate.Loader.Loader.LoadEntity(NHibernate.Engine.ISessionImplementor, System.Object, NHibernate.Type.IType, System.Object, System.String, System.Object, NHibernate.Persister.Entity.IEntityPersister)+f3 
NHibernate.Loader.Entity.AbstractEntityLoader.Load(NHibernate.Engine.ISessionImplementor, System.Object, System.Object, System.Object)+22 
NHibernate.Loader.Entity.AbstractEntityLoader.Load(System.Object, System.Object, NHibernate.Engine.ISessionImplementor)+12 
NHibernate.Persister.Entity.AbstractEntityPersister.Load(System.Object, System.Object, NHibernate.LockMode, NHibernate.Engine.ISessionImplementor)+69 
NHibernate.Event.Default.DefaultLoadEventListener.LoadFromDatasource(NHibernate.Event.LoadEvent, NHibernate.Persister.Entity.IEntityPersister, NHibernate.Engine.EntityKey, NHibernate.Event.LoadType)+84 
NHibernate.Event.Default.DefaultLoadEventListener.DoLoad(NHibernate.Event.LoadEvent, NHibernate.Persister.Entity.IEntityPersister, NHibernate.Engine.EntityKey, NHibernate.Event.LoadType)+1d7 
NHibernate.Event.Default.DefaultLoadEventListener.Load(NHibernate.Event.LoadEvent, NHibernate.Persister.Entity.IEntityPersister, NHibernate.Engine.EntityKey, NHibernate.Event.LoadType)+5e 
NHibernate.Event.Default.DefaultLoadEventListener.ReturnNarrowedProxy(NHibernate.Event.LoadEvent, NHibernate.Persister.Entity.IEntityPersister, NHibernate.Engine.EntityKey, NHibernate.Event.LoadType, NHibernate.Engine.IPersistenceContext, System.Object)+73 
NHibernate.Event.Default.DefaultLoadEventListener.ProxyOrLoad(NHibernate.Event.LoadEvent, NHibernate.Persister.Entity.IEntityPersister, NHibernate.Engine.EntityKey, NHibernate.Event.LoadType)+cb 
NHibernate.Event.Default.DefaultLoadEventListener.OnLoad(NHibernate.Event.LoadEvent, NHibernate.Event.LoadType)+120 
NHibernate.Impl.SessionImpl.FireLoad(NHibernate.Event.LoadEvent, NHibernate.Event.LoadType)+140 
NHibernate.Impl.SessionImpl.Get(System.String, System.Object)+148 
NHibernate.Impl.SessionImpl.Get(System.Type, System.Object)+121 
NHibernate.Impl.SessionImpl.Get[[System.__Canon, mscorlib]](System.Object)+143 
Intellitive.Data.Repositories.NHibernateRepository`1[[System.__Canon, mscorlib]].GetReference(System.Object)+38 

Có nhiều điều sau cuộc gọi đến GetReference nhưng nó không thực sự liên quan đến vấn đề từ những gì tôi có thể nói?

+0

Bạn có chắc chắn rằng ActionFilter của bạn không được gọi khi css/js/images được yêu cầu? Nếu đúng như vậy, thì đó có thể là nguyên nhân của phí (tạo giao dịch và nhấn cơ sở dữ liệu để kiểm tra vai trò cho mọi yêu cầu đó). – ialekseev

+0

Tôi tin như vậy, tôi có thiết lập định tuyến của MVC để bỏ qua tất cả nội dung trong đường dẫn Nội dung và Tập lệnh. Và không có phiên đăng nhập cho nhibernate được hiển thị trong nHProf. –

+0

Bạn đang sử dụng phiên bản NHibernate nào? – b2zw2a

Trả lời

4

Với tôi có vẻ như bạn đang sử dụng NHibernate cũ hơn 4.0.0 (phát hành ngày 17 tháng 8 năm 2014). Nếu bạn đang sử dụng phiên bản mới hơn, chỉ cần bỏ qua câu trả lời này.

Có một vấn đề đồng thời với NHibernate - xem here:

Đôi khi quá trình IIS của chúng tôi bắt đầu sử dụng 100% CPU. Trong một bãi chứa bộ nhớ , chúng ta thấy rằng rất nhiều chủ đề nằm trong phương thức FindEntry từ điển, được gọi từ ColumnNameCache.GetIndexForColumnName.

Điều này đã được giải quyết nhưng bản vá chỉ được hợp nhất thành phiên bản 4.0.0.

Vấn đề là chung Dictionary đang đi vào vòng lặp vô hạn khi bộ sưu tập cơ bản đã được sửa đổi, tức là hai chuỗi đang cố gắng đọc giá trị và một chuỗi đang ghi.

Từ documentation:

Một điển có thể hỗ trợ nhiều độc giả đồng thời, miễn là bộ sưu tập không được sửa đổi. Mặc dù vậy, việc liệt kê thông qua bộ sưu tập thực chất không phải là thủ tục an toàn chỉ. Trong trường hợp hiếm hoi mà một liệt kê có liên quan đến quyền ghi, bộ sưu tập phải được khóa trong toàn bộ liệt kê. Để cho phép bộ sưu tập được truy cập bởi nhiều luồng để đọc và viết, bạn phải triển khai đồng bộ hóa của riêng mình.

Thread-an toàn phiên bản: https://github.com/nhibernate/nhibernate-core/blob/3.4.x/src/NHibernate/AdoNet/ColumnNameCache.cs

Cùng với bản vá áp dụng: giải thích https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/AdoNet/ColumnNameCache.cs

Còn về việc tại sao điển không phải là thread-safe và tại sao IIS dừng yêu cầu phục vụ:

  1. http://blogs.msdn.com/b/tess/archive/2009/12/21/high-cpu-in-net-app-using-a-static-generic-dictionary.aspx
  2. http://improve.dk/debugging-in-production-part-2-latent-race-condition-bugs/
  3. ASP.NET Hang - Generic Dictionary concurrency issues causes GC deadlock
+0

Ahhh, Ok. Điều đó nghe có vẻ như vấn đề tôi đang gặp phải, và lệnh gọi 'GetIndexForColumnName' hiện diện trong cả hai dấu vết ngăn xếp trong câu hỏi của tôi cũng như những thứ khác mà tôi đã chụp nhưng không đăng). Tôi sẽ xem xét việc nâng cấp tham chiếu và thực hiện nHibernate lên nHibernate 4.0 càng sớm càng tốt. –

+2

Chỉ cần một cách nhanh chóng FYI, chúng tôi đã hoàn thành việc chuyển/kiểm tra các trang web với nHibernate 4,0 tuần trước, xuất bản và cho đến nay chưa có vấn đề này kể từ đó. Chúng tôi đã trong quá trình thực hiện nó khi tôi đăng lần cuối; cho đến nay rất tốt, có vẻ như nó giải quyết được vấn đề. :-) –

1

Vấn đề này sẽ có một thủ phạm: các Company.ApplicationServices.SecurityService, mà đời sẽ quá vượt qua vòng đời WebRequest/ISession.

Một số giả định

Phương pháp SecurityService đang làm some magical(không hiển thị trong quesiton trên) cuộc gọi đến ISession, recieving Login đối tượng.

Trong trường hợp Đăng nhập có chứa Nhóm (lần lặp đầu tiên), nó tiếp tục lặp lại và tải nhiều Vai trò.

public IList<Role> GetRoles(string username) 
{ 
    // the instance of login is loaded, still referencing some ISession 
    var login = GetLoginForUser(username); 
    return !login.Groups.Any() // first iteration over Groups 
     ? new List<Role>() 
     // second iteration 
     : login 
      .Groups 
      .SelectMany(x => x.Roles) // other iterations 
      .OrderBy(x => x.DisplayName) 
      .ToList(); 
} 

Cuộc gọi này rõ ràng là rất tốn kém so với quan điểm tiêu thụ tài nguyên. Vì vậy, phải có một số bộ nhớ đệm ở đâu đó (giữ vai trò cho mỗi chủ đề ít nhất, nhưng có thể lâu hơn)

Rất có thể SecurityService là một singleton. Nhưng điều đó có nghĩa là, nó có ISession riêng của mình. Và như vậy ISession hầu như không phụ thuộc vào (các) Yêu cầu Web. Và điều đó có nghĩa, rằng nó có thể có phiên lâu dài.

Nó có thể xảy ra, mà nó trả về cùnglogin dụ hai nhiều đề (nhiều yêu cầu Web xử lý bởi chủ đề khác nhau) khác nhau

tương tự câu chuyện:

tôi sẽ đề nghị xem qua Q & A và thảo luận của nó: Nhibernate Lazy Load exception after a view exception. Vấn đề, nguyên nhân sự cố, khác nhau, nhưng giải pháp phải giống nhau.

Góp ý:

Các giả định trên, ngay cả khi họ là chính xác chỉ một phần, nên giúp đỡ để hiểu gợi ý dưới đây. Tóm lại, chúng ta nên tránh chia sẻ bất kỳ đối tượng từ một ISession ... với chủ đề/yêu cầu khác ...

Những gì chúng ta có thể thấy, là vấn đề với iterating đối tượng đến từ một ISession, bên trong trong số các chủ đề khác.Giải pháp tôi thích: sử dụng Prototype pattern. (Pattern sovling vấn đề tốn kém sáng tạo đối tượng/tải)

  • hãy tải Roles bằng tên người dùng trong một bắn
  • bản sao họ (nguyên mẫu) vào mức độ chúng ta cần
  • bộ nhớ cache họ , nhái sau trả lại cache
  • giới thiệu một số làm mới (sau 2 phút, sql bộ nhớ cache dependeny, tập tin phụ thuộc) để giữ đối tượng an ninh trong bộ nhớ cache trong một thời gian ngắn/hợp lý chỉ

Thậm chí có thể có các cách tiếp cận khác (ví dụ: init tất cả các thuộc tính với NHibernateUtil.Initialize() và bỏ qua Clone) ... nhưng tôi có thể xác nhận rằng Cloning đang hoạt động tốt cho tôi.

Nhanh chóng tổng quan:

class Group : ICloneable 
{ 
    ... 
    public override object Clone() 
    { 
     var entity = base.Clone() as Group; 
     entity.Roles = new List<Role>(); 
     foreach(var r in Roles) 
     { 
      entity.Roles.Add(r.Clone() as Role); 
     } 
     ... 
     return entity; 
    } 
} 

class Login: ICloneable 
{ 
    ... 
    public override object Clone() 
    { 
     var entity = base.Clone() as Login; 
     entity.Groups = new List<Group>(); 
     foreach(var g in Group) 
     { 
      entity.Groups.Add(r.Clone() as Group); 
     } 
     ... 
     return entity; 
    } 
} 

Nice là, rằng dự thảo này trên nằm trong tay chúng tôi. Chúng ta có thể điều chỉnh phần Nhân bản như chúng ta cần. Cuối cùng chúng ta có thể có một Clone, nạp một lần, không phụ thuộc vào bất kỳ phiên, có chỉ thuộc tính cần thiết cho an ninh ... đã sẵn sàng cho bộ nhớ đệm

mở rộng: Dựa trên biết thêm thông tin trong câu hỏi mở rộng

tôi sẽ nói , mà có liên quan đến dự đoán của tôi ở trên, có là thủ phạm (ít nhất là nghi ngờ):

vai trò được lưu trữ ...

Nhưng những vai trò là RELA ted đến một phiên. Chúng không bị tách rời. Họ đâu chỉ nhận được qua LINQ trên đầu trang của Login dụ trả về bởi ISession:

login.Groups.SelectMany(x => x.Roles).OrderBy(x => x.DisplayName).ToList() 

Mỗi đối tượng (Đăng nhập, Group, Role) vẫn còn dính với phiên trong đó nó đã sinh ra.

Trong khi đó, cùng lúc, các yêu cầu web khác nhau sẽ đến. Yêu cầu web khác nhau cho cùng một thông tin đăng nhập (một phần lượt xem, cuộc gọi API Web). Vì vậy, trong môi trường nhiều luồng, nhiều yêu cầu web hơn có thể chạm vào vai trò được lưu trong bộ nhớ cache và làm việc với chúng. Với những cái mà vẫn còn liên quan đến hiện tại, mở ISession, nhưng trên một chủ đề khác nhau.

Nhiều khả năng khuôn khổ bạn đang sử dụng những mạnh mẽ để quyết định những gì để thể hiện và những gì để giấu, những gì để chỉnh sửa ... đối tượng

Vì vậy, trong số rất nhiều các yêu cầu web, có được chia sẻ (tập các đối tượng) liên quan đến một ISession.

Đề xuất của tôi: Tách các đối tượng này. Và cách tôi thấy chính xác hơn là mẫu Protype.

Vì vậy, tôi tin rằng điều này sẽ cung cấp cho bạn thông tin chi tiết về cách những vấn đề này có thể xảy ra, nhưng điều tôi muốn nhấn mạnh là giải pháp. Không chia sẻ các đối tượng liên quan đến một phiên trong số các yêu cầu/chủ đề khác. Bản sao chỉ là một cách. Nhưng hiệu trưởng là chìa khóa.

+0

Tôi đã thêm một số chi tiết ở trên về cách phạm vi của tôi được thực hiện cho hầu hết các khía cạnh bạn đã đề cập. Tôi không thấy vấn đề với cách cấu hình hiện tại, nhưng tôi rất gần với vấn đề này, tôi không chắc chắn tôi sẽ thấy nó nếu có, và rõ ràng là có vấn đề ở đâu đó, hoặc tôi 'd không có tình trạng khó khăn hiện tại của tôi: -P –

+0

Nhân bản có thể là một tùy chọn, nhưng tôi muốn thử và tìm ra lý do tại sao cùng một cá thể đang được chuyển trả lại cho nhiều yêu cầu trên các luồng/yêu cầu khác nhau. Từ giao diện của dấu vết ngăn xếp, đó là.ToList() gây ra vấn đề vì kết quả của SelectMany() của tôi là cùng một bộ sưu tập, nhưng tôi đã nghi ngờ kết quả này là khác nhau cho mỗi luồng/yêu cầu vì phạm vi trên ISession, hoặc là nó đệm kết quả trong nội bộ và trả lại cùng một bộ sưu tập? –

+0

Từ quan điểm của tôi, giải pháp quan trọng nhất là giải pháp. Tôi đã cố gắng để cung cấp thêm chi tiết trong phần mở rộng của tôi, nhưng trong một vỏ hạt: tách bất kỳ đối tượng mà bạn đặt trong bộ nhớ cache. Cách của tôi là sử dụng nhân bản ... –

Các vấn đề liên quan