Sitecore WFFM IsRemoteActions and Scaling Considerations

One of our Sitecore implementations was experiencing WFFM exceptions when trying to send emails from CD servers.  This post about WFFM configuration over on the Community site for Sitecore ends with a vague answer to fix a scenario such as this via the following:

Please remove the below mentioned setting from “Sitecore.Forms.Config” from all CD instance.

<setting name=”WFM.IsRemoteActions” value=”true” />

we have also added this setting as per sitecore documents but after adding this line on CD instance, mail won’t trigger. You have to remove this setting from “Sitecore.Forms.Config”. Making value as “false” won’t fix the issue.

Unfortunately, this is a fairly common scenario on that Community site . . . there are certainly answers tucked away in there if you’re diligent in your research, but sometimes they can leave you with more questions.  I think the new StackExchange site for Sitecore is a breath of fresh air on this front, but for this specific question about WFFM I was left to puzzle over this WFM.IsRemoteActions setting.  Here’s what I uncovered.

The official Sitecore documentation for setting up WFFM explicitly states (on page 6 of the WFFM 8.1 Update-3 installation guide in our case, but it’s mostly the same for other versions):

In the \Website\App_Config\Include\Sitecore.Forms.Config file,
  • Add the following node to the section:
  • <settings> section: <setting name=”WFM.IsRemoteActions” value=”true” />
The above contradicts the guidance on the Sitecore Community forum, and in our case we found that removing this setting altogether DID correct our problem of WFFM emails not being sent.
I used Reflector (like always!) so get some insight into what was going on, and in the Sitecore.Forms.Core.Handlers.FormDataHandler type is an ExecuteSaveActions method that is the crux of the logic for processing forms that are submitted through WFFM.  Right near the top of this method is a big conditional test based around the WFM.IsRemoteActions setting:
wffm
When one removes this setting from a CD server, this logic short-circuits this ExecuteSaveActions method and prevents the CD node from following through on the WFFM activities . . . and instead the Sitecore EventQueue of the “Core” database is used to store a record of this WFFM action.
At a later point, a server that shares that Sitecore “Core” database AND is configured with the proper WFFM hooks and events definition (from Sitecore’s WFFM documentation on Multiserver configuration) as follows . . .
The  section: 
<!--HOOKS-->
<hooks>
<!--remote events hook-->
<hook type="Sitecore.Form.Core.WffmActionHook, Sitecore.Forms.Core"/>
</hooks>

The  section: 
The <events> section:
<!--Remote events handler-->
<event name="wffm:action:remote">
<handler type="Sitecore.Form.Core.WffmActionHandler, Sitecore.Forms.Core"
method="OnWffmActionEventFired" />
</event>  
  . . . will pick up the EventQueue record and run the WFFM event.  Usually this is the CM server.  This is how it all worked in our case.

By removing (or setting to false) this WFM.IsRemoteActions setting we’re asking less of our Sitecore CD servers and centralizing the WFFM save behaviour to the CM server.  Most Sitecore implementations are eager to find ways to reduce the demands on the CD nodes in an implementation, so this measure could be considered a best-practice if you’re using WFFM and looking to save Sitecore CD cycles (CPU!) for responding to HTTP requests for site visitors instead of performing WFFM post save actions.  To me, it looks like you could take this further and delegate this responsibility to a “processing” server if you’ve got one in your scaled Sitecore architecture . . . or any node that suits you.

Powershell installation of Sitecore Performance Counters

Today was the day I bit the bullet and automated this bit of administrative work that we do for all our Sitecore projects. I think it was the fresh crop of 9 new Sitecore IIS nodes that needed this attention that was the final impetus.

Sitecore Performance Counters

This blog reviews all the perf counters Sitecore makes available.  The documentation is a bit thin from Sitecore, since these counters were added to the product several years ago and they apparently don’t get much attention.  They do require special effort to setup as part of your IIS environment, too, so it’s easy to overlook them.  They’re still very handy, though!  A future blog post could discuss the ones most worthy of attention (in my humble opininion).

You can download the .zip file with an installer for the Sitecore specific performance counters at http://sdn.sitecore.net/upload/sdn5/faq/administration/sitecorecounters.zip.  These files are what you’ll end up with:

counters.JPG

The .EXE runs through all the .XML files in the directory that enumerate perf counters relevant to Sitecore, and adds the counter to the computer.  It’s easy.

Scripted Installation

One would typically click the SitecoreCounters.EXE, press the {enter} key, and then the performance counters would quickly install to the server.  Doing this for many servers, or as part of an automated provisioning effort, could be tedious . . . we actually invoke this via remote magic with Ansible (but that’s not the focus of this blog post).  I do want to go over how we automated this, however, since it wasn’t the smooth sailing I expected it to be.

I’ve posted a Powershell baseline for installing Sitecore perf counters to this github gist.  It was straight-forward Powershell until I found the .EXE was running properly, but no performance counters were actually being added.  I had to look at the decompiled .EXE source to figure out why.  Here’s the Main method of the .EXE; I’ve bolded the instruction that was giving me problems:

        private static void Main(string[] args)
        {
            Console.WriteLine("Press enter to begin creating counters");
            Console.ReadLine();
            Console.WriteLine("Creating Sitecore counters");
            string[] files = args;
            if (args.Length == 0)
            {
                files = Directory.GetFiles(Environment.CurrentDirectory, "*counter*.xml");
            }
            foreach (string str in files)
            {
                new Counters(str).Execute();
            }
            Console.WriteLine("Done");
            Console.ReadLine();
        }

The “Environment.CurrentDirectory” in Powershell is by default something like C:\Users\UserName, but my Powershell was using a staging location to download and unzip the perf counter executable and related xml:

$zipFileURI = "https://your.cdn.with.the.Sitecore.zip.resources/sitecorecounters%207.5.zip"
$stageFolder = "C:\staging" 
if( !(test-path $stageFolder) )
{
    mkdir $stageFolder
}
$downLoadZipPath = $stageFolder + "/SitecoreCounters.zip"  
Invoke-WebRequest -Uri $zipFileURI -OutFile $downLoadZipPath
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory($downLoadZipPath, $stageFolder)

Based on what I saw in Reflector, I needed to change my staging directory or to assign the Environment.CurrentDirectory to be $stageFolder in order for the perf counters to properly be installed to the new environment.  I really wanted to use a configurable location instead of some default Powershell directory, so I sifted through Powershell documentation and figured out the magic API call was [System.IO.Directory]SetCurrentDirectory; this example makes it apparent:

Write-Output "Default directory:"
 [System.IO.Directory]::GetCurrentDirectory()
 [System.IO.Directory]::SetCurrentDirectory($stageFolder + "\sitecorecounters 7.5")
 Write-Output "Directory changed to:"
 [System.IO.Directory]::GetCurrentDirectory()

With that in place, the context of the Sitecore perf counter .EXE was set properly and the program could locate all the counters defined in the XML files.

The end result is our new Sitecore specific perf counters are ready:

counters2.JPG

You will need to recycle the Sitecore App Pool in IIS for this to take effect, something like this should suffice:

$theSite = "Cool Sitecore Site"
$thePool = (Get-Item "IIS:\Sites\$theSite"| Select-Object applicationPool).applicationPool
Restart-WebAppPool $thePool