StrataFrame Forum

Index was outside the bounds of the array - stress test

http://forum.strataframe.net/Topic30107.aspx

By ChanKK - 6/29/2011

Hi
I have code as below 

 public virtual void Write(string userName, DateTime dated, string objectName, string action, string hostName)
        {
            using (ActivityLogsBO logger = new ActivityLogsBO())
            {
                //logger.DataSourceKey = this._dataSource;

                logger.Write(userName, dated, objectName, action, hostName);
            }
        }

//--AcitivityBO.cs
public void Write(string userName, DateTime logDate, string objectName, string action, string hostName)
        {
            this.NewRow();
            this.Username = userName;
            this.DateTime = logDate;
            this.ObjectName = objectName;
            this.Action = action;
            this.HostName = hostName;
            SaveUndoResult result = this.Save();

            if (result != SaveUndoResult.Success)
            {
                throw new ApplicationException("Failed to activity log error:" + userName + action);
            }
        }



When I run stress test to it about 30 vusers, it will hit error as below



Exception information: 
    Exception type: DataLayerSavingException 
    Exception message: Index was outside the bounds of the array. 
 
Request information: 
    Request URL: http://kkchan-pc/flexhr_6244/Security/Forms/UserLogin.aspx 
    Request path: /flexhr_6244/Security/Forms/UserLogin.aspx 
    User host address: 192.168.2.100 
    User:  
    Is authenticated: False 
    Authentication Type:  
    Thread account name: IIS APPPOOL\FlexHR_6244AppPool 
 
Thread information: 
    Thread ID: 21 
    Thread account name: IIS APPPOOL\FlexHR_6244AppPool 
    Is impersonating: False 
    Stack trace:    at MicroFour.StrataFrame.Data.DataLayer.UpdateDataTable(DataTable TableToUpdate, Boolean Transactional, String TransactionKey)
   at MicroFour.StrataFrame.Data.DataLayer.Save(DataTable TableToSave, Boolean Transactional, String TransactionKey)
   at MicroFour.StrataFrame.Business.BusinessLayer.Save(Boolean Transactional, String TransactionKey)
   at MicroFour.StrataFrame.Business.BusinessLayer.Save()
   at Vfs.Security.Common.ActiveUserController.Register(String SessionId, ISecurityUserEx CurrentUser)
   at Vfs.Security.Common.Extensions.LoggedInUserEx.SetLoggedOnUser(SFSUsersBO UserInfo, String ClientIP)
   at Vfs.Security.Common.SFSecurityBasics.SetLoggedOnUser(SFSUsersBO Users, String ClientIP)
   at Vfs.Security.UI.Web.LoginController.AttempLogin(String userName, String password, String domainName) in D:\FlexHR\Branches\6.2.0\Security\UI\Web\Controllers\LoginController.cs:line 659
   at Vfs.Security.UI.Web.Controls.LoginContainer.AuthenticateLogin() in D:\FlexHR\Branches\6.2.0\Security\UI\Web\Controls\LoginContainer.cs:line 243
   at Vfs.Security.UI.Web.Controls.LoginContainer.LoginButton_Click(Object sender, EventArgs e) in D:\FlexHR\Branches\6.2.0\Security\UI\Web\Controls\LoginContainer.cs:line 236
   at System.EventHandler.Invoke(Object sender, EventArgs e)
   at DevExpress.Web.ASPxEditors.ASPxButton.OnClick(EventArgs e)
   at DevExpress.Web.ASPxEditors.ASPxButton.RaisePostBackEvent(String eventArgument)
   at DevExpress.Web.ASPxClasses.ASPxWebControl.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument)
   at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)
   at System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData)
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)



Any ideas? Please advice.
By ChanKK - 6/29/2011

Hi
I tried to put debug code and found that the error happen at SqlDataSourceItem.UpdateRow() at line

Me._CachedInsertCommands.Add(RowToUpdate.Table, loCommand)

Please advice.
By ChanKK - 6/30/2011

Hi
I think I found the problem. I fixed it, and tested with stress test, working fine till this moment. Just to share if anyone face the same issue

Root Cause: This class is initialized only once per data source. It is initialized and stored in static field. Therefore, it need to support thread-Safe. Under UpdateRow() method, objects _CachedInsertCommands, _CachedUpdateeCommands and _CachedDeleteCommands are designed to support thread-safe by using SyncLock statement. At EndUpdateCommandCaching() method, it will remove some elements from objects as mentioned. However, it is not written with thread-safe support.
Symptom: Only happen under stress test, frequent of reproduced not consistent.



Suggested Solutions:

Class : SqlDataSourceItem.cs

 Public Overrides Sub EndUpdateCommandCaching(ByVal UpdatingTable As System.Data.DataTable, ByVal Transactional As Boolean)
            '-- Establish locals
            Dim loCommand As SqlCommand

            SyncLock Me._CachedDeleteCommands

                '-- Clear out the dictionary entries
                If Me._CachedDeleteCommands.ContainsKey(UpdatingTable) Then
                    '-- Close the connection on the command
                    loCommand = Me._CachedDeleteCommands(UpdatingTable)

                    '-- Make sure that we were not on a transaction
                    If (Not Transactional) AndAlso (loCommand.Connection IsNot Nothing) Then
                        loCommand.Connection.Close()
                        loCommand.Connection.Dispose()
                    End If
                    loCommand.Dispose()

                    '-- Remove it from the dictionary
                    Me._CachedDeleteCommands.Remove(UpdatingTable)
                End If

            End SyncLock



            SyncLock Me._CachedInsertCommands

                If Me._CachedInsertCommands.ContainsKey(UpdatingTable) Then
                    '-- Close the connection on the command
                    loCommand = Me._CachedInsertCommands(UpdatingTable)

                    '-- Make sure that we were not on a transaction
                    If (Not Transactional) AndAlso (loCommand.Connection IsNot Nothing) Then
                        loCommand.Connection.Close()
                        loCommand.Connection.Dispose()
                    End If
                    loCommand.Dispose()

                    '-- Remove it from the dictionary
                    Me._CachedInsertCommands.Remove(UpdatingTable)
                End If

            End SyncLock



            SyncLock Me._CachedUpdateCommands

                If Me._CachedUpdateCommands.ContainsKey(UpdatingTable) Then
                    '-- Close the connection on the command
                    loCommand = Me._CachedUpdateCommands(UpdatingTable)

                    '-- Make sure that we were not on a transaction
                    If (Not Transactional) AndAlso (loCommand.Connection IsNot Nothing) Then
                        loCommand.Connection.Close()
                        loCommand.Connection.Dispose()
                    End If
                    loCommand.Dispose()

                    '-- Remove it from the dictionary
                    Me._CachedUpdateCommands.Remove(UpdatingTable)
                End If

            End SyncLock

        End Sub



Hope this helps

Thank you