¿Cómo limito el número de llamadas API / filas actualizadas en una declaración UPDATE?

Invantive SQL facilita la actualización y sincronización masiva de datos, ya que la técnica subyacente queda oculta. Con la instrucción update y/o la instrucción synchronize puede actualizar fácilmente datos de forma masiva en aplicaciones en la nube, de modo que todos sus procesos de negocio utilicen los mismos datos.

Algunas aplicaciones en la nube limitan, por razones técnicas, de uso justo y/o comerciales, el número de llamadas a la API. A menudo, al superar el límite se muestra un mensaje de error como «Too many requests» o un código de estado HTTP 429.

El término habitual para este tipo de limitación de llamadas a la API es «rate limit» (límite de velocidad). A veces se aplican varios límites de velocidad simultáneamente, por ejemplo por día, por mes, por minuto, por API, por operación de API (lectura/escritura) o por resultado de la API: con o sin mensaje de error.

En muchos casos es posible comprar llamadas adicionales a la API para aumentar el límite de velocidad, ya sea mediante pago anticipado o facturación posterior. En algunas plataformas, ampliar el límite de llamadas a la API a límites más altos o adicionales resulta relativamente caro, con picos de hasta varias decenas de euros por llamada en el caso de solicitudes complejas; en otras, en cambio, es extremadamente económico.

Es recomendable utilizar el espacio disponible en los límites de velocidad de llamadas a la API en los momentos adecuados, cuando las cantidades están limitadas por unidad de tiempo, como por mes o por día. Realice los ajustes de baja prioridad al final del periodo para obtener el máximo rendimiento de su inversión en la plataforma en la nube.

Varíe el número de llamadas a la API en el tiempo solo después de haber limitado primero el número de llamadas a la API; puede encontrar consejos funcionales al respecto en Hoe plan ik mijn Exact Online-omgeving op het beschikbare aantal API-calls? y consejos técnicos, por ejemplo, en Snel en met minder API-calls Exact Online uitlezen met de incrementele "sync API"-tabellen.

Invantive SQL ofrece funciones para utilizar de la forma más eficaz posible el espacio disponible en los límites de velocidad de llamadas a la API. En la mayoría de las plataformas, el espacio disponible es visible en columnas de la vista del diccionario de datos SystemPartitions@DataDictionary. En ella se encuentran columnas para los límites de velocidad por día, hora y minuto, así como el número de solicitudes realizadas en el día, la hora y el minuto actuales.

Para la instrucción synchronize puede especificar cuántas mutaciones se realizan como máximo. Sin embargo, esto también es posible con las instrucciones SQL tradicionales update, delete e insert.

El número máximo de mutaciones para update, delete e insert puede configurarse mediante limit NÚMERO, de la misma forma que limit limita el número de filas de una selección mediante select.

Esto se ilustra a continuación mediante un caso práctico con la API de Exact Online.

Caso práctico: Modificación de los anexos de facturas

En el sistema de contabilidad Exact Online, puede definir por relación dentro de una administración en qué formatos se envían las facturas. Puede tratarse de una combinación de formatos, que se combinan en un número mediante la llamada «AND bit a bit» (bitwise AND). Por ejemplo, el valor «1» significa solo facturas de venta en formato PDF, el valor «2» significa formato UBL y el valor «3» significa tanto formato PDF como UBL.

En una base de datos de Exact Online con administraciones que tienen cada una 50.000 relaciones, es necesario convertir en todas las administraciones el formato de los anexos de facturas de PDF más UBL a solo PDF. Sin embargo, el número de llamadas a la API por día en Exact Online es solo una fracción de las 50.000 llamadas necesarias, y la conversión tiene una prioridad más baja que los procesos diarios.

Por ello, al final del día se aplican las mutaciones en Exact Online para consumir, con estas solicitudes adicionales, el presupuesto de llamadas a la API que pueda quedar.

Esta mutación tiene la máxima prioridad en los clientes de venta; las demás relaciones aún no tienen estado de cliente.

En el trabajo, el número de mutaciones en Exact Online puede limitarse a 10 mediante la siguiente actualización:

use SELECTIE_ADMINISTRATIES@eol

update exactonlinerest..accounts@eol
set    InvoiceAttachmentType = 1
where  InvoiceAttachmentType = 3
and    status = 'C'
limit  100

Observe que en este caso no se utiliza la tabla AccountsIncremental de Exact Online; esta es de solo lectura y requeriría una instrucción synchronize más compleja, mientras que, gracias al filtrado en el lado del servidor de Invantive y al filtro de Exact Online, la cláusula where puede pasarse directamente a Exact Online, de modo que solo se necesita 1 llamada a la API de Exact Online para solicitar las filas que deben actualizarse.

Esta instrucción, si aún quedan clientes que requieren un ajuste, se completará en aproximadamente 3 segundos.

Un posible remanente del presupuesto de llamadas a la API puede utilizarse para configurar también el tipo de anexos de factura para los que no son clientes:

use SELECTIE_ADMINISTRATIES@eol

update exactonlinerest..accounts@eol
set    InvoiceAttachmentType = 1
where  InvoiceAttachmentType = 3
limit  100

Refinamiento: más administraciones

La modificación masiva puede ampliarse fácilmente a varias administraciones. Para ello, especifique primero la selección de administraciones, por ejemplo para todas las administraciones de Exact Online:

use all@eol

update exactonlinerest..accounts@eol
set    InvoiceAttachmentType = 1
where  InvoiceAttachmentType = 3
and    status = 'C'
limit  1000

update exactonlinerest..accounts@eol
set    InvoiceAttachmentType = 1
where  InvoiceAttachmentType = 3
limit  1000

Dado que en Exact Online los límites de la API actualmente se aplican por administración, es posible establecer un límite más alto. En este ejemplo se ha partido de 10 administraciones, en las que las 1000 actualizaciones se repartirán aproximadamente entre las 10 gracias al procesamiento paralelo, a menos que la cantidad de actualizaciones de API necesarias por administración varíe de forma significativa.

Refinamiento: solo el espacio libre de llamadas a la API en el menor tiempo posible

Un refinamiento más avanzado consiste en limitar el número de llamadas a la API a la cantidad disponible para una administración, además de minimizar el tiempo de ejecución (sin paralelización).

El siguiente bloque de Invantive PSQL dispara repetidamente series cortas de actualizaciones, cuya longitud se elige de modo que el límite por minuto de Exact Online de 60 llamadas a la API por minuto se complete lo más rápido posible:

declare
  l_did_work  boolean;
  l_dummy_cnt number;
  g_max_call_per_minute pls_integer := 50 /* Un poco menos de 60 para poder cambiar con más frecuencia. */;
begin
  use all@eol;
  --
  -- Asegúrese de que las cifras de llamadas a la API restantes estén actualizadas
  -- tocando brevemente todas las administraciones.
  --
  select /*+ http_disk_cache(false) http_memory_cache(false) */ count(*)
  into   l_dummy_cnt
  from   exactonlinerest..journals@eol
  ;
  l_did_work := true;
  while l_did_work
  loop
    dbms_output.put_line('Durante un minuto, se intenta alcanzar los límites por minuto de llamadas a la API por administración.');
    l_did_work := false;
    for r
    in 
    ( select 'update exactonlinerest..accounts@eol'
              || ' set InvoiceAttachmentType = 1'
              || ' where  InvoiceAttachmentType = 3'
              || ' limit  ' 
              || to_char
                 ( case
                   when DAILY_REMAINING > g_max_call_per_minute
                   then g_max_call_per_minute
                   else DAILY_REMAINING
                        --
                        -- Dejar 500 llamadas a la API de Exact Online disponibles 
                        -- para prioridades inesperadas más adelante hoy.
                        --
                        - 500
                   end
                 )
              stmt
       ,      'Actualizar como máximo ' 
              || g_max_call_per_minute 
              || ' filas en la administración ' 
              || code
              || ' - ' 
              || name 
              txt
       from   SYSTEMPARTITIONS@DataDictionary
       where  IS_SELECTED = true
       and    PROVIDER_NAME = 'ExactOnlineAll'
       and    PROVIDER_DATA_CONTAINER_ALIAS = 'eol'
       --
       -- Debe quedar al menos un remanente de 500 llamadas a la API
       -- hoy para esta administración.
       --
       and    DAILY_REMAINING > 500
    )
    loop
      dbms_output.put_line(r.txt);
      execute immediate r.stmt;
      l_did_work := true;
    end loop;
  end loop;
end;