Multiple concurrent requests for the same user (session)

Purpose

Avoid problems when processing multiple concurrent requests for the same user session.

Solutions

Top-level rendering lock 

 uPortal2 synchronized request processing on a session-scoped rendering lock object. This prevents processing of more than one requests at a time for the same session.

Low-level rendering syncronization

Alternatively, the rendering locks can be moved further into the request processing cycle or eliminated completely. For this, the components performing the rendering must delineate variables associated with processing of different requests, and must be generally thread-safe.
In terms of implementation, this approach requires stateful objects involved in the request processing to be request scoped (or thread-local), and the rest of the code to be thread-safe.

Use cases

Opening portions of request processing logic to the concurrent access can be expensive in terms of resources (e.g. creating an instance for each request instead of reusing one instance), and in terms of time required for testing (thread safety issues). Therefore it's important to identify potential use cases, and adjust the rendering synchronization at an appropriate stage of the request processing.

Downloading

Downloading a file from the portal results in a long request processing cycle, during which the user may need to interact with the portal. Therefore one requires an ability to process download requests in parallel with other requests.

AJAX rendering of multiple portlets

It may be desirable to load a portal page leaving out the portlets and then load all of the portlets with multiple asynchronous requests to the portal. In that case an early rendering lock would result in a serial rendering of the portlets, which is undesirable. Therefore there needs to be a way to render individual portlets in a parallel fashion.

Double click

If a user submits two closely followed requests (e.g. by double-clicking a link) it will take twice as much time to render. It's possible to reduce that time by making as much of request processing as possible unsynchronized.

Components that don't support concurrent requests

Below is a quick list of known problematic component classes with the variables that should be request-scoped.

TransformationFilter

  • transformerHandler
  • urlCacheKeys
  • receiveSAXPipelineEvent() typically sets up threadlocal stuff.

Within SAX rendering, there are also some state variables that are set based on the incoming SAX events.
Those are obviosuly thread-scoped:

ElementRenderingAttributeInjector:

firstElement

LegacyTransientLayoutInjector:

writtenOut

SaxToStringCacheFilter:

currentWriter
serializerContentHandler
serialzierLexicalHandler
xmlSerializer
currentKey
currentEntry

StringResponseWriter

currentResponse

BasePortletFilter

currentRequest
currentResponse

ResponseSerializerImpl (not being used in a current configuration)

ServletResponse res;
ContentHandler currentContentHandler;
LexicalHandler currentLexicalhandler;
DTDHandler currentDtdHandler;
String currentSerializerName;
Serializer currentSerializer;

SaxCacheFilterImpl (not being used in a current configuration)

buffer
currentKey
currentEntiry
currentDemanded

StringCacheFilterImpl

currentKey
currentEntiry
currentDemanded

StringResponseWriter

currentResponse

PortletWindowManager

portlet window rendering params (from current and previous requests) are stored in a session map (key PORTLET_RENDERING_PARAM_MAP_SESSION_KEY).
This will have to be refactored to store current request rendering params in a request attribute, and only use session map as a source
of previous request params (for refresh requests, and such).

Portlet rendering (multithreaded)

All URL constructors will have to be checked (I looked by only found the known ServletPathExtender problem)
Once we do this, there's an obvious question of thread safety for all other components that are being called (i.e. everything else (smile) ...
We should think about costs here.

Solution

With the current feature set, it should be possible to synchronize on a rendering lock at point where the rendering pipeline is invoked (CompoundContext.render() method, implemented by hybridPipeline bean in uP2 context). This will ensure that processing of all the rendering components is safe. The request processing part (parameter parsing, portlet aciton processing, etc.) will be left open to the concurrent access and should be thoroughly checked (also, see portlet window manager issue descirbed in the previous section).