ColdFusion 变量竞争条件?
ColdFusion VARIABLES Race Condition?
我需要一些帮助来确定为什么这个特定代码在极少数情况下会产生竞争条件。我找到了一个修复程序,我也会概述它,但我真的很想了解它。
我们有一个基于 CMS 的系统,该系统由许多松散地基于保险丝盒模型的模块组成。一切都通过一个 index.cfm.
在我们的 Index.cfm 中,我们正在创建几个组件实例,其中一些基于在 Application.cfc 中创建的 APPLICATION.PortalApp 实例。我没有包含该代码,因为它并不完全相关:
<cfset REQUEST.ActionHandler = CreateObject("Component", "Components.ActionHandler").init(APPLICATION.PortalApp.Config) />
<cfset VARIABLES.Modules = CreateObject("Component", "Components.Modules").init(APPLICATION.PortalApp.Config, REQUEST.ActionHandler.GetModuleList(), REQUEST.ActionHandler.GetSuppressOutput(), REQUEST.ActionHandler.GetRoleList(), REQUEST.ActionHandler.GetAccessList(), REQUEST.ActionHandler.GetMasterRoleList()) />
实例化这些对象后,我们通过调用作为申请,Application.PortalApp.
<cfsavecontent variable="Variables.Portal_Content.Top"><cfset APPLICATION.PortalApp.PageManager.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 0 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Left"><cfset APPLICATION.PortalApp.PageManager.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 1 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Middle"><cfset APPLICATION.PortalApp.PageManager.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 2 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Right"><cfset APPLICATION.PortalApp.PageManager.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 3 ) /></cfsavecontent>
PageManager.DisplayContent 基本上遍历模块并将它们包装在包装器中。但是,在某些时候,存在竞争条件并且函数崩溃并且根本不显示任何模块。它似乎是基于 VARIABLES.Modules 变得腐败,但那是在不共享的 VARIABLES 范围内。
为了修复它,我们将代码更改为以下内容:
<!--- If we do not use VARIABLES scope and create a ContentManager, race condition can cause empty modules --->
<cfset VARIABLES.CurrPageMgr = CreateObject("Component", "Components.ContentManager").init() />
<cfsavecontent variable="Variables.Portal_Content.Top"><cfset VARIABLES.CurrPageMgr.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 0 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Left"><cfset VARIABLES.CurrPageMgr.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 1 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Middle"><cfset VARIABLES.CurrPageMgr.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 2 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Right"><cfset VARIABLES.CurrPageMgr.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 3 ) /></cfsavecontent>
ContentManager 的 DisplayContent 与 PageManager.DisplayContent 的功能文本完全相同,但 ContentManager 仅存在于 VARIABLES 范围内,而 PageManager 则作为 APPLICATION 的一部分存在。
收到报告后很难重现。我基本上启动了一个 jmeter 会话,使用基于我知道会触发 VARIABLES.Module 损坏的条件设置的断点,尽可能地重击开发服务器。这是复制它的唯一方法。
我也不是 100% 确定这个修复有效,但到目前为止 jmeter 还没有触发条件。
编辑:根据请求,DisplayContent 函数:
<cffunction name="displayContent" access="public" output="true">
<cfargument name="SessionData" required="yes" type="Struct" />
<cfargument name="ActionHandler" required="yes" type="ActionHandler" />
<cfargument name="Modules" required="yes" type="Modules" />
<cfargument name="Pane" required="yes" type="numeric" />
<cfswitch expression="#Arguments.Pane#">
<cfcase value="1"><cfset Variables.blnPane = ARGUMENTS.Modules.getLeft()></cfcase>
<cfcase value="2"><cfset Variables.blnPane = ARGUMENTS.Modules.getCenter()></cfcase>
<cfcase value="3"><cfset Variables.blnPane = ARGUMENTS.Modules.getRight()></cfcase>
<cfdefaultcase><cfset Variables.blnPane = ARGUMENTS.Modules.getTop()></cfdefaultcase>
</cfswitch>
<cfif VARIABLES.blnPane>
<cfset VARIABLES.qryPaneModules = ARGUMENTS.Modules.GetModulesInPane(Arguments.Pane)>
<cfset VARIABLES.aryModulesInPane = ArrayNew(1)>
<cfloop query="VARIABLES.qryPaneModules">
<cfset VARIABLES.blnResult = ArrayAppend(aryModulesInPane,VARIABLES.qryPaneModules.MOD_SYS_NR)>
</cfloop>
<cfset VARIABLES.Template = "../CustomTags/Portalv#ARGUMENTS.SessionData.intPortalVersion#/DisplayModuleAlternate.cfm">
<cfif Arguments.ActionHandler.GetDocumentType() EQ 3>
<cfset VARIABLES.Template = "../CustomTags/Portalv#ARGUMENTS.SessionData.intPortalVersion#/DisplayXMLModule.cfm">
</cfif>
<cfif VARIABLES.qryPaneModules.recordcount GT 0>
<cfloop index="VARIABLES.modLoop" from="1" to="#ArrayLen(VARIABLES.aryModulesInPane)#">
<cfparam name="VARIABLES.aryModulesInPane[VARIABLES.modLoop]" default="0">
<cfset VARIABLES.objModuleInfo = ARGUMENTS.Modules.GetModuleInfo( VARIABLES.aryModulesInPane[VARIABLES.modLoop], ARGUMENTS.Modules.GetModules() ) />
<cfif NOT IsNumeric(VARIABLES.objModuleInfo.intModuleID)>
<cfset VARIABLES.objModuleInfo.intModuleID = 0 >
</cfif>
<cfmodule template="#VARIABLES.Template#"
ModuleID="#VARIABLES.objModuleInfo.intModuleID#"
ModuleName="#VARIABLES.objModuleInfo.strModuleName#"
SecurityLevel="#VARIABLES.objModuleInfo.intRoleTypeID#"
ModuleDSN="#VARIABLES.objModuleInfo.strModDBDSN#"
ModuleUserName="#VARIABLES.objModuleInfo.strModDBUserID#"
ModulePassword="#VARIABLES.objModuleInfo.strModDBPassword#"
AlternateFunctionID="#VARIABLES.objModuleInfo.intAlternateFunctionID#"
AlternateFunctionName="#VARIABLES.objModuleInfo.strAlternateFunctionName#"
InstructionFileID="#VARIABLES.objModuleInfo.intManualID#"
ModuleOps="#VARIABLES.objModuleInfo.blnModuleOps#"
ModuleSource="#VARIABLES.objModuleInfo.strModuleSource#"
ItemID="#VARIABLES.objModuleInfo.intModItemID#"
AutoLoginID="#VARIABLES.objModuleInfo.intAutoLoginCategoryID#"
IPS_objPortalSessionData="#ARGUMENTS.SessionData#"
ModuleList="#ARGUMENTS.ActionHandler.GetModuleList_SingleRecord(VARIABLES.objModuleInfo.intModuleID)#"
ModulesComponent="#ARGUMENTS..Modules#"
ControlHeaderIR = "#VARIABLES.objModuleInfo.blnControlHeaderIR#"
BorderIR = "#VARIABLES.objModuleInfo.blnBorderIR#"
IPS_strPortalRoot = "#VARIABLES.IPS_strPortalRoot#"
IPS_strPortalURL = "#VARIABLES.IPS_strPortalURL#"
Wrapper = "#Arguments.ActionHandler.getWrapper()#"
Definition = "#VARIABLES.objModuleInfo.strModuleDef#"
Category = "#VARIABLES.objModuleInfo.strModuleCat#"
aryModulesInPane = "#VARIABLES.aryModulesInPane#"
blnLockIR = "#VARIABLES.objModuleInfo.blnLockIR#"
strMessageTE = "#VARIABLES.objModuleInfo.strMessageTE#" >
</cfloop>
</cfif>
</cfif>
</cffunction>
在 PageManager 组件的 DisplayContent 函数中使用 VARIABLES 范围(作为应用程序的一部分实例化,因此共享)是问题所在。在这种情况下将共享 VARIABLES 范围,从而导致竞争条件。
除了在另一个组件中复制该函数并在 index.cfm 的 VARIABLES 范围内实例化它之外,您还可以从 DisplayContent 函数中的 VARIABLES 范围切换到 LOCAL 范围(假设您没有需要在函数外访问这些变量)。这两种方式都可以防止在使用 jmeter 进行压力测试时重新出现竞争条件。
我需要一些帮助来确定为什么这个特定代码在极少数情况下会产生竞争条件。我找到了一个修复程序,我也会概述它,但我真的很想了解它。
我们有一个基于 CMS 的系统,该系统由许多松散地基于保险丝盒模型的模块组成。一切都通过一个 index.cfm.
在我们的 Index.cfm 中,我们正在创建几个组件实例,其中一些基于在 Application.cfc 中创建的 APPLICATION.PortalApp 实例。我没有包含该代码,因为它并不完全相关:
<cfset REQUEST.ActionHandler = CreateObject("Component", "Components.ActionHandler").init(APPLICATION.PortalApp.Config) />
<cfset VARIABLES.Modules = CreateObject("Component", "Components.Modules").init(APPLICATION.PortalApp.Config, REQUEST.ActionHandler.GetModuleList(), REQUEST.ActionHandler.GetSuppressOutput(), REQUEST.ActionHandler.GetRoleList(), REQUEST.ActionHandler.GetAccessList(), REQUEST.ActionHandler.GetMasterRoleList()) />
实例化这些对象后,我们通过调用作为申请,Application.PortalApp.
<cfsavecontent variable="Variables.Portal_Content.Top"><cfset APPLICATION.PortalApp.PageManager.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 0 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Left"><cfset APPLICATION.PortalApp.PageManager.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 1 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Middle"><cfset APPLICATION.PortalApp.PageManager.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 2 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Right"><cfset APPLICATION.PortalApp.PageManager.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 3 ) /></cfsavecontent>
PageManager.DisplayContent 基本上遍历模块并将它们包装在包装器中。但是,在某些时候,存在竞争条件并且函数崩溃并且根本不显示任何模块。它似乎是基于 VARIABLES.Modules 变得腐败,但那是在不共享的 VARIABLES 范围内。
为了修复它,我们将代码更改为以下内容:
<!--- If we do not use VARIABLES scope and create a ContentManager, race condition can cause empty modules --->
<cfset VARIABLES.CurrPageMgr = CreateObject("Component", "Components.ContentManager").init() />
<cfsavecontent variable="Variables.Portal_Content.Top"><cfset VARIABLES.CurrPageMgr.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 0 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Left"><cfset VARIABLES.CurrPageMgr.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 1 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Middle"><cfset VARIABLES.CurrPageMgr.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 2 ) /></cfsavecontent>
<cfsavecontent variable="Variables.Portal_Content.Right"><cfset VARIABLES.CurrPageMgr.DisplayContent( SESSION, REQUEST.ActionHandler, VARIABLES.Modules, 3 ) /></cfsavecontent>
ContentManager 的 DisplayContent 与 PageManager.DisplayContent 的功能文本完全相同,但 ContentManager 仅存在于 VARIABLES 范围内,而 PageManager 则作为 APPLICATION 的一部分存在。
收到报告后很难重现。我基本上启动了一个 jmeter 会话,使用基于我知道会触发 VARIABLES.Module 损坏的条件设置的断点,尽可能地重击开发服务器。这是复制它的唯一方法。
我也不是 100% 确定这个修复有效,但到目前为止 jmeter 还没有触发条件。
编辑:根据请求,DisplayContent 函数:
<cffunction name="displayContent" access="public" output="true">
<cfargument name="SessionData" required="yes" type="Struct" />
<cfargument name="ActionHandler" required="yes" type="ActionHandler" />
<cfargument name="Modules" required="yes" type="Modules" />
<cfargument name="Pane" required="yes" type="numeric" />
<cfswitch expression="#Arguments.Pane#">
<cfcase value="1"><cfset Variables.blnPane = ARGUMENTS.Modules.getLeft()></cfcase>
<cfcase value="2"><cfset Variables.blnPane = ARGUMENTS.Modules.getCenter()></cfcase>
<cfcase value="3"><cfset Variables.blnPane = ARGUMENTS.Modules.getRight()></cfcase>
<cfdefaultcase><cfset Variables.blnPane = ARGUMENTS.Modules.getTop()></cfdefaultcase>
</cfswitch>
<cfif VARIABLES.blnPane>
<cfset VARIABLES.qryPaneModules = ARGUMENTS.Modules.GetModulesInPane(Arguments.Pane)>
<cfset VARIABLES.aryModulesInPane = ArrayNew(1)>
<cfloop query="VARIABLES.qryPaneModules">
<cfset VARIABLES.blnResult = ArrayAppend(aryModulesInPane,VARIABLES.qryPaneModules.MOD_SYS_NR)>
</cfloop>
<cfset VARIABLES.Template = "../CustomTags/Portalv#ARGUMENTS.SessionData.intPortalVersion#/DisplayModuleAlternate.cfm">
<cfif Arguments.ActionHandler.GetDocumentType() EQ 3>
<cfset VARIABLES.Template = "../CustomTags/Portalv#ARGUMENTS.SessionData.intPortalVersion#/DisplayXMLModule.cfm">
</cfif>
<cfif VARIABLES.qryPaneModules.recordcount GT 0>
<cfloop index="VARIABLES.modLoop" from="1" to="#ArrayLen(VARIABLES.aryModulesInPane)#">
<cfparam name="VARIABLES.aryModulesInPane[VARIABLES.modLoop]" default="0">
<cfset VARIABLES.objModuleInfo = ARGUMENTS.Modules.GetModuleInfo( VARIABLES.aryModulesInPane[VARIABLES.modLoop], ARGUMENTS.Modules.GetModules() ) />
<cfif NOT IsNumeric(VARIABLES.objModuleInfo.intModuleID)>
<cfset VARIABLES.objModuleInfo.intModuleID = 0 >
</cfif>
<cfmodule template="#VARIABLES.Template#"
ModuleID="#VARIABLES.objModuleInfo.intModuleID#"
ModuleName="#VARIABLES.objModuleInfo.strModuleName#"
SecurityLevel="#VARIABLES.objModuleInfo.intRoleTypeID#"
ModuleDSN="#VARIABLES.objModuleInfo.strModDBDSN#"
ModuleUserName="#VARIABLES.objModuleInfo.strModDBUserID#"
ModulePassword="#VARIABLES.objModuleInfo.strModDBPassword#"
AlternateFunctionID="#VARIABLES.objModuleInfo.intAlternateFunctionID#"
AlternateFunctionName="#VARIABLES.objModuleInfo.strAlternateFunctionName#"
InstructionFileID="#VARIABLES.objModuleInfo.intManualID#"
ModuleOps="#VARIABLES.objModuleInfo.blnModuleOps#"
ModuleSource="#VARIABLES.objModuleInfo.strModuleSource#"
ItemID="#VARIABLES.objModuleInfo.intModItemID#"
AutoLoginID="#VARIABLES.objModuleInfo.intAutoLoginCategoryID#"
IPS_objPortalSessionData="#ARGUMENTS.SessionData#"
ModuleList="#ARGUMENTS.ActionHandler.GetModuleList_SingleRecord(VARIABLES.objModuleInfo.intModuleID)#"
ModulesComponent="#ARGUMENTS..Modules#"
ControlHeaderIR = "#VARIABLES.objModuleInfo.blnControlHeaderIR#"
BorderIR = "#VARIABLES.objModuleInfo.blnBorderIR#"
IPS_strPortalRoot = "#VARIABLES.IPS_strPortalRoot#"
IPS_strPortalURL = "#VARIABLES.IPS_strPortalURL#"
Wrapper = "#Arguments.ActionHandler.getWrapper()#"
Definition = "#VARIABLES.objModuleInfo.strModuleDef#"
Category = "#VARIABLES.objModuleInfo.strModuleCat#"
aryModulesInPane = "#VARIABLES.aryModulesInPane#"
blnLockIR = "#VARIABLES.objModuleInfo.blnLockIR#"
strMessageTE = "#VARIABLES.objModuleInfo.strMessageTE#" >
</cfloop>
</cfif>
</cfif>
</cffunction>
在 PageManager 组件的 DisplayContent 函数中使用 VARIABLES 范围(作为应用程序的一部分实例化,因此共享)是问题所在。在这种情况下将共享 VARIABLES 范围,从而导致竞争条件。
除了在另一个组件中复制该函数并在 index.cfm 的 VARIABLES 范围内实例化它之外,您还可以从 DisplayContent 函数中的 VARIABLES 范围切换到 LOCAL 范围(假设您没有需要在函数外访问这些变量)。这两种方式都可以防止在使用 jmeter 进行压力测试时重新出现竞争条件。