Exact Online bereikt daglimiet sinds Invantive Data Hub 26.0.X

Sinds de 26.0.X versie van Data Hub lopen we tegen het bereiken van de dagelijkse limiet van de API calls aan voor een Exact Online-dossier. Voordien was dit nog niet het geval.

Momenteel analyseren we waar het probleem zit, in het vernieuwingsschema was de SalesOrderLines endpoint nog opgenomen met 312k+ rijen wat een logische oorzaak vormde voor een hoog verbruik. Deze werd voorlopig uit de vernieuwing gehaald.

Doch lopen we terug tegen de limiet aan, hieronder een overzicht van de endpoints en een ruwe inschatting van het verwachtte aantal calls.Dit resulteert in een verwacht aantal calls van om en bij de 3.4k (in acht genomen dat hier waarschijnlijk nog andere calls rond hangen voor refresh tokens e.d.).

Hierdoor blijven we naar inschatting onder de limiet van 5k per dag, zien we iets over het hoofd waardoor het aantal calls drastisch hoger zou zijn dan de inschatting?

tabel tpn_count_rows Results per call? API Calls needed?
ExactOnlineREST.PurchaseOrder.GoodsReceiptLines 20182 60 336
ExactOnlineREST.PurchaseOrder.GoodsReceipts 1750 60 29
ExactOnlineREST.Purchase.PurchaseInvoices 1038 60 17
ExactOnlineREST.Purchase.PurchaseInvoiceLines 20053 60 334
ExactOnlineREST.Logistics.ItemAssortments 10 60 0
ExactOnlineREST.Logistics.ItemAssortmentProperties 558 60 9
ExactOnlineREST.Financial.Journals 25 60 0
ExactOnlineREST.Financial.FinancialPeriods 72 60 1
ExactOnlineREST.Incremental.GLAccountsIncremental 830 1000 1
ExactOnlineREST.Incremental.TransactionLinesIncremental 492070 1000 492
ExactOnlineXML.XML.Items 8588 60 143
ExactOnlineREST.Logistics.ItemsBulk 8588 1000 9
ExactOnlineXML.XML.AROutstandingItems 427 60 7
ExactOnlineREST.Subscription.SubscriptionLineTypes 2 60 0
ExactOnlineREST.Subscription.SubscriptionLines 2 60 0
ExactOnlineREST.Subscription.Subscriptions 2 60 0
ExactOnlineREST.Subscription.SubscriptionTypes 4 60 0
ExactOnlineREST.HRM.Costunits 43 60 1
ExactOnlineREST.HRM.Costcenters 4 60 0
ExactOnlineREST.SalesInvoice.SalesInvoicesBulk 22474 1000 22
ExactOnlineREST.SalesInvoice.SalesInvoiceLinesBulk 326384 1000 326
ExactOnlineREST.SalesOrder.GoodsDeliveryLinesBulk 298456 1000 298
ExactOnlineREST.SalesOrder.GoodsDeliveriesBulk 35471 1000 35
ExactOnlineREST.SalesOrder.SalesOrders 27720 60 462
ExactOnlineREST.SalesOrder.SalesOrdersBulk 27720 1000 28
ExactOnlineREST.SalesOrder.SalesOrderLinesBulk 312594 1000 313
ExactOnlineREST.CRM.AccountClassifications 37 60 1
ExactOnlineREST.CRM.AddressesBulk 11560 1000 12
ExactOnlineREST.CRM.ContactsBulk 4415 1000 4
ExactOnlineREST.CRM.AccountsBulk 10330 1000 10
ExactOnlineREST.Logistics.SupplierItems 7989 60 133
ExactOnlineREST.PurchaseOrder.PurchaseOrders 1040 60 17
ExactOnlineREST.PurchaseOrder.PurchaseOrderLines 19986 60 333

De daadwerkelijke API-calls zijn terug te vinden in het scherm “Sessie-I/O’s” op Invantive Cloud. Hier zijn ook on-premises metingen in verwerkt.

Advies is om deze te gebruiken om de grootste consumenten te achterhalen.

“Sessie-I/O’s” wijzen op volgende inzichten, dit gefilterd op de partitie van de probleemadministratie.

Overzicht aantal aanwezige I/O’s gegroepeerd volgen “Tabelnaam”.

Rijlabels Aantal van Sorteervolgorde
ExactOnlineREST.Incremental.TransactionLinesIncremental (new/modified) 999
ExactOnlineREST.SalesOrder.SalesOrders 926
ExactOnlineREST.PurchaseOrder.GoodsReceiptLines 674
ExactOnlineREST.Purchase.PurchaseInvoiceLines 670
ExactOnlineREST.SalesInvoice.SalesInvoiceLinesBulk 654
ExactOnlineREST.SalesOrder.SalesOrderLinesBulk 626
ExactOnlineREST.SalesOrder.GoodsDeliveryLinesBulk 598
ExactOnlineREST.PurchaseOrder.PurchaseOrderLines 317
ExactOnlineXML.XML.Items 172
ExactOnlineREST.SalesOrder.GoodsDeliveriesBulk 72
ExactOnlineREST.PurchaseOrder.GoodsReceipts 60
ExactOnlineREST.SalesOrder.SalesOrdersBulk 56
ExactOnlineREST.SalesInvoice.SalesInvoicesBulk 46
ExactOnlineREST.Purchase.PurchaseInvoices 36
ExactOnlineREST.Logistics.ItemAssortmentProperties 20
ExactOnlineREST.Logistics.ItemsBulk 18
ExactOnlineREST.Workflow.CustomerCooperations 12
ExactOnlineREST.Incremental.TransactionLinesIncremental/$count 6
ExactOnlineREST.Incremental.TransactionLinesIncremental (deleted) 4
ExactOnlineREST.Incremental.GLAccountsIncremental/$count 4
ExactOnlineREST.Incremental.TransactionLinesIncremental (metadata) 4
ExactOnlineREST.Financial.FinancialPeriods 4
ExactOnlineREST.Incremental.GLAccountsIncremental (new/modified) 2
ExactOnlineREST.Subscription.SubscriptionLineTypes 2
ExactOnlineREST.Logistics.ItemAssortments 2
ExactOnlineREST.Subscription.SubscriptionLines 2
ExactOnlineREST.Incremental.GLAccountsIncremental (metadata) 2
ExactOnlineREST.Subscription.Subscriptions 2
ExactOnlineREST.Incremental.GLAccountsIncremental (timemachine) 2
ExactOnlineREST.Financial.Journals 2
ExactOnlineXML.XML.AROutstandingItems 2
ExactOnlineREST.Incremental.GLAccountsIncremental (deleted) 2
ExactOnlineREST.HRM.Costcenters 2
ExactOnlineREST.Subscription.SubscriptionTypes 2
ExactOnlineREST.HRM.Costunits 2
Eindtotaal 6004

Volgende valt op:

ExactOnlineREST.Incremental.TransactionLinesIncremental (new/modified)

  • Deze valt hoog uit en is niet verwacht
  • Deze refresh wordt geïnitieerd middels volgend statement:
select /*+ ods(true, interval ‘1 seconds’) */ count(*) 
from   ExactOnlineREST.Incremental.TransactionLinesIncremental@eolbe;
  • Vormt dit een probleem waardoor deze zo hoog uitvalt?

Bulk-tabellen met een hoog aantal calls

  • 654 calls op ExactOnlineREST.SalesInvoice.SalesInvoiceLinesBulk
  • Deze tabel heeft in lijn met de initiële tabel om en bij de 300k records.
  • Hier zou ik dusdanig 300 calls verwachten, gezien een bulk tabel 1000 record oplevert per call?

Advies is om de aantallen API-calls aan te sluiten bij het aantal rijen. Dat kan snel via de tips op:

Een *Incremental zal normaliter pakweg 1.000 API-calls alleen doen bij initiele load of als er iets mis is (bijvoorbeeld te grote deviatie tussen wat in cache staat en wat aantal rijen is).

Uit de 4 API-calls bij (deleted) is in ieder geval af te lezen dat er vier keer een volledige actualisatie heeft plaatsgevonden in de gemeten periode.

Dat kan geanalyseerd worden door in Sessie-I/O’s te kijken hoe laat en binnen welke sessie-ID de calls plaatsvinden.

Iets vergelijkbaars geldt voor *Bulk-tabellen: in het scherm Sessie-I/O’s biedt aanknopingspunten om te achterhalen wanneer er ververst wordt. Anders dan bij *Incremental zal echter iedere verse laadactie (uitgaande van 300K rijen in SalesInvoiceLinesBulk) circa 300 API-calls veroorzaken. Als een seconde later nogmaals dezelfde query draait, dan genereert dat ook weer pakweg 300 API-calls, etc.

Dit kan aansluiten bij de 4 API-calls bij (deleted). Mogelijk dat een script herhaaldelijk heeft gedraaid binnen de gemeten periode.

Het script start op 07.30 CET op via Windows Taakplanner en maakt hierbij een log bestand, op serverniveau kan ik maar één log bestand vinden.

De I/O’s zijn allemaal afkomstig van de run dat deze ochtend gestart werd en sluiten qua tijdstip aan met de logs:

Start:

2026-07-03 05:30:12.399 Information itgensql264: Invantive UniversalSQL statement started.

Stop:

2026-07-03 09:20:22.922 Error itgencun016: Error itgeneor229: There is no daily capacity remaining available for Exact Online API use on Exact Online division ‘……’.
  Please optimize your API use, or contact Exact Online Support to acquire more API calls per day than the current limit of 5 000 calls, or try again tomorrow.

Het bestand met de queries volgt volgende opbouw:

use 123@eolbe;
SELECT /*+ ods(true, interval '1 seconds') */ count(*) FROM ExactOnlineREST.SalesOrder.SalesOrderLinesBulk@eolbe;
SELECT /*+ ods(true, interval '1 seconds') */ count(*) FROM ExactOnlineREST.SalesOrder.SalesOrdersBulk@eolbe;
... other tables ...

use 456@eolbe;
SELECT /*+ ods(true, interval '1 seconds') */ count(*) FROM ExactOnlineREST.SalesOrder.SalesOrderLinesBulk@eolbe;
SELECT /*+ ods(true, interval '1 seconds') */ count(*) FROM ExactOnlineREST.SalesOrder.SalesOrdersBulk@eolbe;
... other tables ...

use 789@eolbe;
SELECT /*+ ods(true, interval '1 seconds') */ count(*) FROM ExactOnlineREST.SalesOrder.SalesOrderLinesBulk@eolbe;
SELECT /*+ ods(true, interval '1 seconds') */ count(*) FROM ExactOnlineREST.SalesOrder.SalesOrdersBulk@eolbe;
... other tables ...

Advies is om de informatie uit de Sessie-I/O’s aan te sluiten bij het gebruik in de logbestanden, waaronder tijdstippen.

Op dit moment is er geen reden om uit te gaan van een bug.

Is er hiertoe een uitgeschreven procedure beschikbaar?

Ik volg nog niet volledig in de concrete stap dat ik moet uitvoeren.

Nee, hier is verder geen procedure of uitwerking van, noch gepland.

Advies is om dan een ervaren consultant te betrekken of via Claude AI om advies te vragen.

Onderzoek met Claude AI werd doorlopen, dit met volgende input:

  • I/O’s van 03/07/2026 sync
  • Log van 03/07/2026 sync (fout)
  • Log van 10/06/2026 sync (succesvol, nog op v24)

Hierbij enkele punten die mogelijks relevant zijn?

Geen 1000 records / call

The fingerprint of the regression is in July's rows-per-call. From the I/O export, every large table in July returns almost exactly ~500 rows per call:

TransactionLines: 492,070 rows / 999 calls = 492/call
SalesOrderLinesBulk: 312,594 / 626 = 499/call
SalesInvoiceLinesBulk: 326,384 / 654 = 499/call
GoodsDeliveryLinesBulk: 298,456 / 598 = 499/call

Exact Online's bulk and sync endpoints return up to 1,000 rows per page. Getting a uniform ~500 across four independent tables isn't coincidence — it's a page size of 500, i.e. half-size pages, double the calls for the same data. Apply that doubling to TransactionLines plus the three bulk tables and you get roughly 1,800–1,900 calls that a 1,000-row page would have served in ~950.

ODS interval 1 sec

Wordt een incrementele tabel steeds aangevuld indien het “/*+ ods(true, interval ‘1 seconds’) */” element achterwege gelaten wordt?

Momenteel zou dit ervoor zorgen dat er steeds een volledige sync plaatsvindt?

Advies is om de relatie te leggen naar tijdstippen; het klinkt als meerdere loads. Exact Online kent alleen door Exact precies voorgeschreven paginagroottes.

De hint ods heeft geen invloed op de logica voor incrementele tabellen of HTTP caches; zie ook:

Bedankt voor de feedback. Ik heb de I/O’s per tijdstip naast het logbestand gelegd zoals geadviseerd, en het beeld van meerdere loads wordt bevestigd. Vandaag loopt de vernieuwing terug en volgen we op of dit terug een limiet zal triggeren.

Inzichten van AI:

Context: Data Hub 26.0.167. De refresh liep op 03/07 van 05:30 tot de crash om 09:20:22 op itgeneor229 (“no daily capacity remaining … the current limit of 5 000 calls”). De I/O-export bevat 6 004 requests, waarvan ~5 990 binnen dit ene venster.

1. Paginagrootte klopt. Per pass haalt Data Hub ~999 rijen/call op (bv. 312 594 rijen / 313 calls), dus de door Exact voorgeschreven ~1 000. Mijn eerdere “500/call” was een telfout: ik deelde het opgeslagen aantal rijen door het totaal aantal calls over twee passes.

2. Elke grote tabel wordt tweemaal opgehaald. Wanneer ik de requests op tijdstip sorteer, valt elke grote tabel uiteen in twee bijna identieke passes met een inactief gat ertussen (0 calls tijdens het gat), terwijl het log slechts één Downloaded … rows registreert:

Tabel Pass 1 Pass 2 Log “Downloaded”
SalesOrderLinesBulk 05:34–05:45 (313 calls / 417 MB) 05:57–06:08 (313 calls / 421 MB) 312 594 rijen (1×)
SalesInvoiceLinesBulk 07:27–07:34 (327 calls) 07:40–07:48 (327 calls) 326 384 rijen (1×)
GoodsDeliveryLinesBulk 07:01–07:11 (299 calls) 07:17–07:26 (299 calls) 298 456 rijen (1×)
TransactionLinesIncremental 08:22–08:25 (500 calls) 08:38–08:42 (499 calls) 492 070 rijen (1×)
XML Items 07:54–07:57 (86 calls) 08:00–08:03 (86 calls)

De twee passes zijn byte-voor-byte symmetrisch (bv. 417 vs 421 MB voor SalesOrderLinesBulk). De data wordt dus één keer opgeslagen, maar twee keer over de lijn gehaald.

3. Impact op de daglimiet. De tweede passes zijn samen ~1 524 calls. Zonder die herhaling komt de run op ~4 480 calls — onder de limiet van 5 000. De overschrijding komt dus niet door paginagrootte of de sync-anchor, maar door de dubbele ophaling.

4. Vergelijking met een geslaagde run (09/06, Data Hub 24.0.445, zelfde ods=1s): het log toont daar één Downloaded … rows per tabel en de run eindigde zonder daglimiet, met vergelijkbare (zelfs grotere) volumes op divisiecode. Kanttekening: dat oudere log bevat geen I/O op requestniveau, dus ik kan het aantal calls voor juni niet exact tellen — de dubbele ophaling is enkel in de 26.0.167-export zichtbaar.

Vraag: kunnen jullie helpen verklaren waarom elke bulk/sync-tabel in twee passes wordt opgehaald? Concreet:

  • Triggeren de ODS-seeding én de daaropvolgende SELECT count(*) elk een aparte ophaling?
  • Waarop wacht de run tijdens het inactieve gat van ~6–12 min tussen de twee passes?

Op basis van de informatie is daar weinig over te zeggen.

Advies is om: