Implementar la memoria caché global y el patrón singleton

Completados

Puede utilizar el almacenamiento en caché para mejorar el rendimiento de su código. Las ventajas aumentan para los datos o códigos utilizados con frecuencia. Puede implementar el almacenamiento en caché global en una sesión de cliente específica o aplicarlo a todos los clientes de un servidor. En algunos escenarios, puede utilizar el almacenamiento en caché global para parámetros adicionales en una firma de método, porque las firmas de método no se pueden cambiar con extensiones.

Importante

Debe gestionar el almacenamiento en caché con cuidado, ya que afecta al consumo de memoria. El almacenamiento en caché global almacena valores en la memoria y, como desarrollador, usted es responsable de actualizar y eliminar datos obsoletos de la memoria caché. Ignorar la limpieza del almacenamiento en caché global puede afectar negativamente al rendimiento.

Las prácticas recomendadas para la implementación del almacenamiento en caché dependen de los casos de uso.

SysGlobalCache y singleton

SysGlobalCache y singleton hacen referencia a una única memoria caché de sesión de cliente a la que no se puede acceder en otros subprocesos del sistema. El sistema limpia esta memoria caché cuando finaliza la sesión del cliente, pero se recomienda limpiarla después de usarla.

Puede crear SysGlobalCache de una instancia de la clase SysGlobalCache, pero le recomendamos que la cree a partir de las siguientes clases del sistema para que la limpieza sea más sencilla:

  • En la clase Application mediante la variable de sistema appl.
  • En la clase ClassFactory mediante la variable de sistema classFactory.
  • En la clase Info mediante la variable de sistema Infolog.

Todas las clases de sistema enumeradas tienen ya definida la opción globalCache, que se puede utilizar.

La clase SysGlobalCache almacena datos en una clave específica y tiene varios métodos de uso común:

  • Construct: le permite crear una nueva instancia de SysGlobalCache. Al utilizar una de las clases del sistema, como Application, ClassFactory o Info, este método crea automáticamente una instancia si aún no existe.
  • Get: recupera un valor de un propietario y una clave específicos.
  • Set: establece un valor en un propietario y una clave específicos.
  • IsSet: comprueba si el caché está establecido para un propietario y una clave específicos.

El sistema almacena valores en SysGlobalCache mediante un propietario y una clave.

En el siguiente ejemplo se muestra cómo verificar si existe un valor, obtener un valor y establecer un valor mediante SysGlobalCache.

internal final class RunnableClass1
{

    /// <summary>
    /// Class entry point. The system will call this method when a designated menu 
    /// is selected or when execution starts and this class is set as the startup class.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {
        const str   mySysGlobalCacheKey   = 'MySysGlobalCacheKey';
        str         mySysGlobalCacheOwner = classStr(RunnableClass1);

        SysGlobalCache cache = classFactory.globalCache();

        container       cacheValue;

        if (cache.isSet(mySysGlobalCacheOwner, mySysGlobalCacheKey))
        {
            cacheValue = cache.get(mySysGlobalCacheOwner, mySysGlobalCacheKey);
        }
        else
        {
            cacheValue = ['MyValue'];

            cache.set(mySysGlobalCacheOwner, mySysGlobalCacheKey, cacheValue);
        }

        cache.remove(mySysGlobalCacheOwner, mySysGlobalCacheKey);

    }

}

Puede lograr el mismo resultado mediante una clase Singleton (para la sesión del usuario), que es una instancia de la clase que el sistema crea dentro de la clase. Un uso común de esta instancia en las aplicaciones de finanzas y operaciones son las clases Flight, tal y como muestra el siguiente código.

/// <summary>
/// When <c>AccountingFinTagFlight</c> is enabled, the uptake of financial tags functionality in the
/// Source Document and Accounting Framework is turned on. This allows documents implementing SDAF
/// to opt into processing of financial tags and provide their values during accounting distribution.
/// </summary>
public final class AccountingFinTagFlight extends Flight
{
    private static AccountingFinTagFlight singleton = new AccountingFinTagFlight();

    [Hookable(false)]
    protected boolean isEnabledByDefault()
    {
        return false;
    }

    [Hookable(false)]
    public static AccountingFinTagFlight instance()
    {
        return singleton;
    }

}

En este ejemplo de la aplicación estándar, el sistema crea una instancia de la clase AccountingFinTagFlight, pero solo una vez para la sesión del usuario.

El caso de uso define el enfoque, y el sistema usa SysGlobalCache y singleton en la aplicación estándar.

SysGlobalObjectCache

SysGlobalObjectCache es un almacenamiento en caché compartido por el servidor en todas las sesiones de usuario en un servidor específico.

La memoria caché SysGlobalObjectCache es un singleton y solo se puede usar una vez en el sistema. Obtenerlo de la clase ClassFactory o mediante la inicialización de una nueva SysGlobalObjectCache siempre hará referencia a la primera instancia creada de la clase SysGlobalObjectCache.

SysGlobalObjectCache se define en un ámbito, en lugar de asociarse a un propietario, como en SysGlobalCache.

El objetivo de usar SysGlobalObjectCache es almacenar valores estáticos en una memoria caché para todas las sesiones, de modo que el sistema no tenga que volver a leerlos ni calcularlos para cada sesión.

En el siguiente ejemplo de aplicaciones de finanzas y operaciones, se muestra una nueva instancia de SysGlobalObjectCache. Sin embargo, si la instancia ya se ha creado, el sistema hará referencia a la clase existente SysGlobalObjectCache, por lo que solo existe una, o un singleton, en el sistema.

public boolean findExternalDescription()
{
    boolean                     found;
    container                   cacheKey;
    container                   cacheValueExistsCheck;
    boolean                     continueProcessing;
    SysGlobalObjectCache        sysGlobalObjectCacheExistCheck = new SysGlobalObjectCache();

    cacheKey = [custVendAccountId, this.parmCustVendItemGroupId(), this.moduleType(), curExt()];

    //Check for existence
    cacheValueExistsCheck = sysGlobalObjectCacheExistCheck.find(CustVendExternalItemDescription::cacheScopeExistsCheck(), cacheKey);

    //We have not checked this combination before
    if (cacheValueExistsCheck == conNull())
    {
        if (CustVendExternalItem::existsForCustVendRelation(custVendAccountId, this.parmCustVendItemGroupId(), this.moduleType()))
        {
            // at least 1 record available for the customer/vendor group or customer/vendor
            continueProcessing = true;
        }
        else
        {
            // no record available for the customer/vendor group or customer/vendor
            continueProcessing = false;
        }
        sysGlobalObjectCacheExistCheck.insert(CustVendExternalItemDescription::cacheScopeExistsCheck(), cacheKey, [continueProcessing]);
    }
    else
    {
        [continueProcessing] = cacheValueExistsCheck;
    }

    if (continueProcessing)
    {
        [found, externalItemId, externalItemFreeTxt, custVendExternalItem] =
                CustVendExternalItemDescription::findExternalItemDescription(this.moduleType(), itemId, inventDim,
                                                                             custVendAccountId,this.parmCustVendItemGroupId());
    }

    return found;
}

Puede eliminar todos los movimientos de SysGlobalObjectCache para un servidor que ejecuta una clase que utiliza la siguiente URL: https://[your finance and operations apps url]/?mi=SysClassRunner&cls=SysFlushAOD.

La clase SysFlushAOD borra toda la memoria caché en SysGlobalObjectCache.

Clases de contexto

Con las extensiones, no puede cambiar la firma del método, lo que significa que no puede agregar nuevos parámetros a un método existente.

Nota

Como se mencionó anteriormente, puede cambiar la firma del método con superposición de capas agregando un nuevo parámetro para un método. Sin embargo, puede ampliar la firma del método mediante un contexto Desechable. Al implementar IDisposable en la clase de contexto, se verá obligado a tener un método de eliminación para la limpieza.

El siguiente ejemplo crea una clase de contexto para SalesPool con un parámetro denominado myParameter.

internal final class MySalesPoolContext
{
    static MySalesPoolContext salesPoolContext;
 
    public  boolean myParameter;
  
    public void new()
    {
        salesPoolContext = this;
    }
 
    public void dispose()
    {
        salesPoolContext = null;
 
    }
  
    static public MySalesPoolContext current()
    {
        return  salesPoolContext;
 
    }

}

Ahora puede agregar una extensión al método find para la tabla SalesPool. Debe probar el nuevo parámetro que ha creado en la clase de contexto sin cambiar la firma del método find, como se ve a continuación:

[ExtensionOf(tableStr(SalesPool))]
final class SalesPool_MB500_Extension
{
    static SalesPool find(SalesPoolId  _salesPoolId,
                          boolean     _forUpdate)
    {
        SalesPool  salesPool = next find(_salesPoolId, _forUpdate);

        MySalesPoolContext  salesPoolContext = MySalesPoolContext::current();

        if (salesPoolContext && salesPoolContext.myParameter)
        {
            // some code...
        }

        return salesPool;
    }

}

Con fines de prueba, en el siguiente ejemplo se crea una nueva clase ejecutable, y puede configurar la variable context parameter para llamar a los métodos de eliminación para limpiar después de usar la clase.

internal final class MySalesPoolContextTest
{
    /// <summary>
    /// Class entry point. The system will call this method when a designated menu 
    /// is selected or when execution starts and this class is set as the startup class.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {
        SalesPool           salesPool;
        MySalesPoolContext  mySalesPoolContext = new MySalesPoolContext();

        mysalesPoolContext.myParameter = true;

        salesPool = SalesPool::find('10');

        if (mySalesPoolContext)
        {
            mySalesPoolContext.dispose();
        }
    }

}

Crear una clase de contexto como singleton le ayuda a transferir parámetros sin cambiar la firma del método.