Wie kann ich die Anzahl der API-Calls / der aktualisierten Zeilen in einer UPDATE-Anweisung begrenzen?

Invantive SQL erleichtert das massenhafte Aktualisieren und Synchronisieren von Daten, da die Technik unter der Haube bleibt. Mit der update-Anweisung und/oder der synchronize-Anweisung können Sie ganz einfach massenhaft Daten in Cloud-Anwendungen aktualisieren, sodass alle Ihre Geschäftsprozesse dieselben Daten verwenden.

Manche Cloud-Anwendungen beschränken aus technischen, Fair-Use- und/oder kommerziellen Gründen die Anzahl der API-Aufrufe. Bei Überschreitung wird häufig eine Fehlermeldung wie „Too many requests“ oder ein HTTP-429-Statuscode zurückgegeben.

Der gängige Begriff für eine solche Beschränkung von API-Aufrufen ist „Rate Limit“. Manchmal gelten mehrere Rate Limits gleichzeitig, zum Beispiel pro Tag, pro Monat, pro Minute, pro API, pro API-Operation (Lesen/Schreiben) oder pro API-Ergebnis: mit oder ohne Fehlermeldung.

Oft können zusätzliche API-Aufrufe hinzugekauft werden, sodass das Rate Limit höher liegt – entweder mit Vorauszahlung oder mit nachträglicher Abrechnung. Bei manchen Plattformen ist die Erweiterung des API-Call-Rate-Limits auf höhere/zusätzliche Grenzwerte relativ teuer, mit Ausreißern bis zu mehreren Dutzend Euro pro Stück bei komplexen Aufrufen, bei anderen extrem spottbillig.

Es ist ratsam, den verfügbaren Spielraum bei Rate Limits hinsichtlich API-Aufrufen zu den richtigen Zeitpunkten einzusetzen, wenn die Anzahl pro Zeiteinheit wie Monat oder Tag begrenzt ist. Anpassungen mit niedriger Priorität führen Sie am Ende der Periode durch, um die maximale Rendite aus Ihrer Investition in die Cloud-Plattform zu erzielen.

Variieren Sie die Anzahl der API-Aufrufe im Zeitverlauf erst, nachdem Sie zunächst die Anzahl der API-Aufrufe begrenzt haben; funktionale Tipps hierzu finden Sie unter Hoe plan ik mijn Exact Online-omgeving op het beschikbare aantal API-calls? und technische Tipps beispielsweise unter Snel en met minder API-calls Exact Online uitlezen met de incrementele "sync API"-tabellen.

Invantive SQL bietet Möglichkeiten, den verfügbaren Spielraum bei Rate Limits hinsichtlich API-Aufrufen so effektiv wie möglich zu nutzen. Der verfügbare Spielraum ist bei den meisten Plattformen in Spalten der Data-Dictionary-View SystemPartitions@DataDictionary sichtbar. Darin befinden sich Spalten für die Rate Limits pro Tag, Stunde und Minute sowie die Anzahl der Anfragen im aktuellen Tag, der aktuellen Stunde und der aktuellen Minute.

Für die synchronize-Anweisung können Sie angeben, wie viele Mutationen maximal stattfinden. Dies ist jedoch auch mit den traditionellen SQL-Anweisungen update, delete und insert möglich.

Die maximale Anzahl an Mutationen für update, delete, insert können Sie über limit ZAHL einstellen, genau wie limit die Anzahl der Zeilen aus einer Auswahl über select begrenzt.

Anhand eines Praxisbeispiels mit der Exact-Online-API wird dies veranschaulicht.

Fallbeispiel: Anpassen von Rechnungsanhängen

Im Buchhaltungssystem Exact Online können Sie pro Beziehung in einer Verwaltung festlegen, in welchen Formaten die Rechnungen versendet werden. Dies kann eine Kombination von Formaten sein, die über das sogenannte „bitwise AND“ zu einer Zahl kombiniert werden. Der Wert „1“ bedeutet beispielsweise nur Verkaufsrechnungen im PDF-Format, der Wert „2“ bedeutet UBL-Format und der Wert „3“ bedeutet sowohl PDF- als auch UBL-Format.

In einer Exact-Online-Datenbank mit Verwaltungen mit jeweils 50.000 Beziehungen muss bei allen Verwaltungen das Format der Rechnungsanhänge von PDF plus UBL auf nur PDF umgestellt werden. Die Anzahl der API-Aufrufe pro Tag bei Exact Online beträgt jedoch nur einen Bruchteil der benötigten 50.000 API-Aufrufe, und die Umstellung hat eine niedrigere Priorität als die täglichen Verarbeitungen.

Daher werden am Ende des Tages die Mutationen in Exact Online durchgeführt, um ein eventuell verbleibendes API-Call-Budget mit diesen zusätzlichen Anfragen zu verbrauchen.

Die höchste Priorität hat diese Mutation bei Verkaufskunden; die übrigen Beziehungen haben noch keinen Kundenstatus.

Im Job kann die Anzahl der Mutationen auf Exact Online über das folgende Update auf 10 begrenzt werden:

use SELECTIE_ADMINISTRATIES@eol

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

Beachten Sie, dass hierbei keine Exact-Online-Tabelle AccountsIncremental verwendet wird; diese ist schreibgeschützt und würde eine komplexere synchronize-Anweisung erfordern, während die where-Klausel dank des serverseitigen Filterns von Invantive und des Filters von Exact Online direkt an Exact Online weitergegeben werden kann, wodurch nur 1 Exact-Online-API-Aufruf nötig ist, um die zu aktualisierenden Zeilen abzufragen.

Diese Anweisung ist – sofern noch Kunden vorhanden sind, bei denen eine Anpassung nötig ist – nach etwa 3 Sekunden fertig.

Ein eventuell verbleibendes API-Call-Budget kann genutzt werden, um auch für die Nicht-Kunden die Art der Rechnungsanhänge einzustellen:

use SELECTIE_ADMINISTRATIES@eol

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

Verfeinerung: mehr Verwaltungen

Das massenhafte Anpassen lässt sich einfach auf mehrere Verwaltungen ausweiten. Geben Sie hierfür zunächst die Verwaltungsauswahl an, zum Beispiel für alle Exact-Online-Verwaltungen:

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

Da bei Exact Online die API-Limits derzeit pro Verwaltung gelten, ist es möglich, das Limit höher einzustellen. In diesem Beispiel wird von 10 Verwaltungen ausgegangen, wobei die 1000 Updates durch die parallele Verarbeitung grob auf alle 10 verteilt werden, es sei denn, die Menge der benötigten API-Updates pro Verwaltung variiert erheblich.

Verfeinerung: nur freien API-Call-Spielraum in möglichst kurzer Zeit

Eine fortgeschrittenere Verfeinerung ist die Begrenzung der Anzahl der API-Aufrufe auf die für eine Verwaltung verfügbare Anzahl, zusammen mit der Minimierung der Durchlaufzeit (ohne Parallelisierung).

Der folgende Invantive-PSQL-Block feuert jeweils kurze Serien von Updates ab, wobei die Länge so gewählt ist, dass das Exact-Online-Minutenlimit von 60 API-Aufrufen pro Minute so schnell wie möglich ausgeschöpft wird:

declare
  l_did_work  boolean;
  l_dummy_cnt number;
  g_max_call_per_minute pls_integer := 50 /* Etwas niedriger als 60, um häufiger wechseln zu können. */;
begin
  use all@eol;
  --
  -- Dafür sorgen, dass die Werte bezüglich verbleibender API-Calls aktuell sind,
  -- indem alle Verwaltungen kurz angesprochen werden.
  --
  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('Versuche während einer Minute, die API-Call-Minutenlimiten pro Verwaltung zu erreichen.');
    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
                        --
                        -- 500 Exact-Online-API-Calls für unerwartete
                        -- Prioritäten später am Tag übrig lassen.
                        --
                        - 500
                   end
                 )
              stmt
       ,      'Aktualisiere maximal ' 
              || g_max_call_per_minute 
              || ' Zeilen in Verwaltung ' 
              || code
              || ' - ' 
              || name 
              txt
       from   SYSTEMPARTITIONS@DataDictionary
       where  IS_SELECTED = true
       and    PROVIDER_NAME = 'ExactOnlineAll'
       and    PROVIDER_DATA_CONTAINER_ALIAS = 'eol'
       --
       -- Es müssen heute noch mindestens 500 API-Calls
       -- für diese Verwaltung übrig sein.
       --
       and    DAILY_REMAINING > 500
    )
    loop
      dbms_output.put_line(r.txt);
      execute immediate r.stmt;
      l_did_work := true;
    end loop;
  end loop;
end;