Scaling out the Viewer Control and rsExecutionNotFound

One of the criteria that the report server uses to match the provided SessionID with a stored report is that the SessionID has to be provided by the same user that initially created the session.  Usually, this is the case.  Someone browses the report in IE, they click around to paginate or expand toggles, and things just work because they are the same user they were when they initially ran the report.

Sometimes though, things go wrong.

One way that this can happen is when you are hosting the ASP.Net Viewer Control in your own application and you are not impersonating the incoming user all the way to the backend report server.  This is a totally supported configuration, however there is a little caveat that you have to keep in mind.  Since the report server requires that the user names match across session retrievals, you have to ensure that the viewer control is impersonating the same user.  Sounds easy, right?  Well not so fast if you are using a machine specific account for your application pool which is hosting the viewer control.  The specific topology that can get you into trouble is something like the following:

 

In this scenario you have the following:

  • Client machines accessing multiple web frontends hosting the report viewer control via a load balancer.
  • The web frontend machines are using a machine-specific account to communicate with the report server (for example they are using the builtin NETWORK SERVICE account).
  • You are not impersonating the incoming user in the viewer control when accessing the backend report server.

The sequence of operations that can lead to trouble are:

  1. The initial request from ClientA is routed to MachineA. 
  2. The viewer control in MachineA instantiates a session with the report server.  Since the $MachineA credentials are sent, the session is associated with this user.  Keep in mind at this point the report server actually has no idea that there is some other logical user beyond the web frontend that is actually making the request.
  3. The user views the first page of the report, and navigates to the second page.
  4. The request for the second page is actually routed to MachineB by the load balancer.
  5. The viewer control in MachineB attempts to load the user session, however this fails since the request to the report server is actually from $MachineB user and not the original $MachineA user.  The report server generates an rsExecutionNotFound error and returns that, resulting in an error being displayed to the user.

There are a couple of ways to address this problem:

  1. Architect your application such that you can flow credentials from the client all the way to the report server.  -- Or --
  2. Ensure that your web frontend nodes can impersonate the same user when accessing the report server regardless of which machine the request is routed to (so use something like domain credentials to access the report server).