Uploaded image for project: 'WildFly'
  1. WildFly
  2. WFLY-17217

Redesign SFSB cache SPI to improve performance of distributed SFSB cache


    • ---
    • ---

      While exploring a new SFSB cache implementation based on a remote Infinispan cluster, I ran into some significant obstacles due to the limitations of the existing SFSB cache SPI from the ejb3 subsystem.

      To summarize the current SPI, a typical use case looks like:

      For SFSB creation:

      StatefulSessionComponentInstance instance = cache.create();

      For SFSB invocations:

      StatefulSessionComponentInstance instance = cache.get(id);

      followed by:






      The primary issue with the existing SPI is that the underlying implementation has no mechanism to retain transient cache state across calls to get(..) -> release(...), since the cached object type is opaque to the cache. To workaround this, we had to introduce an interface that allows the cache implementation to attach an arbitrary context to the StatefulSessionComponentInstance (see WFLY-6224).

      The distributable cache implementation has an additional complication: it needs to retain object references between all SFSBs referenced from (i.e. injected into) a given SFSB following replication or persistence. To do this, during SFSB creation, it collects all StatefulSessionComponentInstances created by a single call to StatefulSessionComponent.createInstance(), and stores them within a single Map<SessionID, StatefulSessionComponentInstance> (referred to as a bean group) referenced by a unique identifier. Additionally, the instances returned by calls to Cache.get(...) for SessionIDs associated with the same group must be obtained from the same Map instance. For the existing embedded Infinispan implementation, we leverage the Infinispan transaction context to achieve this. Unfortunately, a remote Infinispan cluster implementation has no such context.

      A secondary issue with the SPI - it is poorly encapsulates the invocation/transaction state of the SFSB instance. The cache implementation is responsible for storing a bean instance's "in use" state, currently achieved via reference counting, so that a bean instance is only considered idle once no longer in use. All other transient invocation state (isDiscarded(), isRemoved(), isSynchronizationRegistered(), etc.) is stored within the StatefulSessionComponentInstance itself. This results in very fragile code - that easily leads to weird state corner cases. Both Cache.create() and Cache.get() return a StatefulSessionComponentInstance, but only the latter call must be released/discarded. Worse, given a StatefulSessionComponentInstance - there is no mechanism to determine its state relative to the SFSB cache.

      An improved version of this SPI would have the Cache return an object with a well defined lifecycle that itself references the StatefulSessionComponentInstance. This object can maintain any cache implementation specific state for the lifetime of the invocation or transaction. Instead of pairing the get/release or get/discard methods, we instead use something like:

      SessionID id = cache.createStatefulSessionBean();
      try (StatefulSessionBean<SessionID, StatefulSessionComponentInstance> bean = cache.findStatefulSessionBean(id)) {
      	StatefulSessionComponentInstance instance = bean.getInstance();

      Since the StatefulSessionBean implementation is cache specific, this allows the distributable cache implementation to store any requisite context within the StatefulSessionBean implementation itself (e.g. transaction context, group context, etc).

        1. main.png
          47 kB
        2. main.zip
          1.03 MB
        3. WFLY-17217.png
          47 kB
        4. WFLY-17217.zip
          1.03 MB

            pferraro@redhat.com Paul Ferraro
            pferraro@redhat.com Paul Ferraro
            0 Vote for this issue
            2 Start watching this issue