Ga naar de hoofdinhoud
Versie: 1.x

Hoe de synchronisatie-engine werkt

Nieuw in v1.10.0

Deze pagina beschrijft de synchronisatie-engine die is geïntroduceerd in WCPOS v1.10.0. Eerdere versies gebruiken een ander replicatiemodel per scherm — zie Wat er is veranderd in v1.10.0 onderaan deze pagina.

WCPOS is local-first: elk scherm leest en schrijft naar een database op het apparaat, en een synchronisatie-engine houdt die database en je WooCommerce-winkel op de achtergrond convergent. Deze pagina legt uit hoe de engine bepaalt wát er wordt opgehaald, wannéér dat gebeurt, en hoe je verkopen terugkomen op de server — het detailniveau dat nuttig is voor ontwikkelaars, integrators en winkeleigenaren die willen begrijpen wat de POS met hun hosting doet.

Eén engine per winkel en kassamedewerker

De POS draait één synchronisatie-engine per combinatie van site + winkel + kassamedewerker op elk apparaat. Die engine heeft zijn eigen lokale database, dus bij het wisselen van winkel of kassamedewerker wisselt het hele gegevensvlak in plaats van dat er wordt gefilterd binnen één gedeelde stapel records. De isolatie per kassamedewerker is bewust: twee kassamedewerkers op hetzelfde apparaat delen nooit lokale gegevens.

Bij upgrades worden lokale databases nooit ter plekke gemigreerd — de app begint met een nieuwe database en downloadt opnieuw vanaf de server, die altijd de gezaghebbende kopie heeft (zie Upgraden vanaf v1.9).

Bij Pro-installaties met meerdere winkels identificeert elk synchronisatieverzoek zijn winkel, zodat een prijs die aan de kassa wordt aangepast de prijs van díé winkel bijwerkt en niet die van de webwinkel.

Wat er op de achtergrond draait

Alles wat de engine volgens een schema doet, is een baan — een benoemde, begrensde eenheid achtergrondwerk. Banen vallen in drie groepen uiteen:

GroepBanenStandaardritme
OphalenWijzigingscontrole (wijzigingssignaal)10 s – 5 min, ingesteld door je synchronisatievoorinstelling
Recente bestellingen, basisvulling productcatalogus, basisvulling referentiegegevens~5 min
Klanten druppelsgewijs (alleen tijdens inactiviteit)~5 min
VersturenWachtrij legen — verstuurt lokale wijzigingen uit de wachtrij~10 s
OnderhoudIntegriteits- en verwijderingsaudits, vernieuwen van servertotalenelke paar minuten tot ~17 min

Voor elke baan gelden twee eigenschappen:

  • Elke baan declareert een maximum aantal verzoeken per run. Geen enkele baan mag in één run een onbegrensd aantal verzoeken uitwaaieren; grotere taken gebruiken begrensde batches met hervatbare cursors. Dit is een centrale ontwerpinvariant, die uitgebreid wordt behandeld in Synchronisatieprestaties.
  • Onderhoud geeft voorrang aan de kassamedewerker. Audits en achtergrondvoorbereiding draaien ná het interactieve werk dat een kassa klaarmaakt om te verkopen, nooit ervoor, en ze worden als eerste gepauzeerd zodra je server tekenen van druk vertoont.

De bovenstaande ritmes zijn standaardwaarden. Het controle-interval en het aantal records per verzoek zijn per apparaat door de verkoper in te stellen via Winkelstatus → Prestaties in de POS — zie Winkelstatus.

Hoe de POS wijzigingen leert kennen

De engine downloadt gegevens niet opnieuw om te ontdekken of ze zijn gewijzigd. De server houdt een wijzigingslog bij, en de POS bevraagt een lichtgewicht wijzigingscontrole die één vraag beantwoordt: is er iets veranderd sinds mijn laatste positie?

  • Eén controle dekt acht collecties — producten, variaties, belastingtarieven, klanten, kortingsbonnen, categorieën, merken en tags. Bestellingen staan bewust niet in de wijzigingscontrole; de actualiteit van bestellingen komt uit een eigen baan voor recente bestellingen, en daarom kunnen product- en bestelupdates in verschillende ritmes binnenkomen.
  • Een inactieve kassa kost bijna niets. De engine stuurt voorwaardelijke verzoeken: als er niets is veranderd, antwoordt de server met één enkele 304 Not Modified zonder body. Een rustige winkel komt uit op één klein antwoord per controle.
  • Controles krijgen ±20% jitter zodat meerdere kassa's op één site uit elkaar lopen in plaats van de server in gesynchroniseerde pieken te raken.
  • Verval bij inactiviteit: na 10 minuten zonder interactie worden de controles verder uit elkaar gezet (met een ondergrens van 60 seconden). Elke echte activiteit — een aanraking, een toetsaanslag, een geaccepteerde barcodescan — schakelt direct terug naar het volledige ritme en start meteen een inhaalcontrole. Het verval rekt het interval alleen op; er wordt nooit sneller bevraagd dan de geconfigureerde instelling.
  • Een kassa die dagenlang dicht was, stelt een nieuwe basislijn in plaats van de geschiedenis opnieuw af te spelen. Als het wijzigingslog te ver voorbij de laatste positie van de kassa is gekomen, zou het opnieuw afspelen van elke regel honderden verzoeken kosten. In plaats daarvan zet de engine zijn cursor naar de kop en controleert opnieuw wat de huidige serverstatus is van wat het apparaat al heeft — zo schaalt de kosten met de omvang van de lokale kopie en niet met hoelang de kassa weg was.

Hoe schermen hun gegevens krijgen

In v1.10.0 draaien schermen niet hun eigen synchronisatie. Een scherm declareert wat het toont — zoekterm, filters, sortering, pagina — en de engine bepaalt of daar überhaupt een verzoek voor nodig is. Elke declaratie wordt op een van drie manieren afgehandeld:

  • Opgehaald — de engine heeft werk over de lijn gedaan om eraan te voldoen.
  • Lokaal beantwoord — het apparaat had het antwoord al, of een identieke recente ophaalactie dekt het af.
  • Vervangen — het scherm ging verder (je scrollde, je wijzigde een filter) en een nieuwere declaratie kwam ervoor in de plaats. Dit is normaal, geen fout.

Een filter dat alleen lokaal beantwoord kan worden, gaat nooit naar de server. En een geslaagde declaratie betekent niet dat een collectie volledig is gedownload — volledigheid wordt apart bijgehouden (volgende sectie).

Niet alles wordt gretig gedownload, en wat er bij het opstarten op het apparaat staat, verschilt per collectie:

  1. Basisvulling — producten komen binnen via een begrensde basisvulling van de catalogus; belastingtarieven worden bij het opstarten opgehaald (een POS kan zonder die tarieven niet rekenen in het winkelwagentje).
  2. Op aanvraag, plus een druppel tijdens inactiviteit — klanten hebben geen gretige basisvulling. De klantendruppel downloadt een kleine batch per inactief interval en slaat die volledig over zodra de kassamedewerker actief is; nieuwe en gewijzigde klanten komen binnen via de wijzigingscontrole.
  3. Opgehaald bij eerste opening — categorieën, tags, merken en kortingsbonnen worden opgehaald wanneer een kassamedewerker ze voor het eerst opent. Een collectie die niemand opent, genereert nooit ook maar één verzoek.

Variatiekiezers vernieuwen daarnaast bij elke opening eenmalig prijs en voorraad, zodat een aanwezige variatie direct wordt weergegeven maar nooit dagenoude voorraad toont.

„Is alles gedownload?” — eerlijke dekking

Een uitlezing als „1.240 van 5.000 producten” heeft een servertotaal als noemer nodig. De engine houdt er één per collectie bij (ongeveer elke 15 minuten vernieuwd, meestal gratis — echte synchronisatieantwoorden bevatten het totaal al, dus een apart verzoek is zelden nodig) en volgt een strikt eerlijkheidscontract:

  • Een verouderd of ontbrekend servertotaal wordt getoond als controleren… — het wordt nooit stilzwijgend vervangen door een lokale telling. Een lokale noemer zou altijd 100% aangeven en precies het gat verbergen dat het getal zichtbaar moet maken.
  • Als de engine niet kan instaan voor de volledigheid, is het oordeel onbekend en labelt de interface het cijfer als een lokale telling.
  • De dekkingsbalk voor bestellingen meet tegen de volledige bestelgeschiedenis op de server, terwijl de kassa bewust alleen openstaande en recente bestellingen bewaart — een gezonde kassa leest daar dus per ontwerp als gedeeltelijk.

Deze cijfers verschijnen op Winkelstatus → Database, samen met de mijlpaal Klaar om te verkopen, die omslaat zodra het eerste product op het apparaat staat — offline zijn blokkeert dat niet, want offline verkopen is juist het doel. Zie Winkelstatus.

Hoe wijzigingen terugkomen in je winkel

Elke lokale schrijfactie — een verkoop, een productbewerking, een klantupdate — komt eerst in een duurzame uitgaande wachtrij op het apparaat, en een baan duwt die wachtrij elke paar seconden naar WooCommerce. Dat is wat offline verkopen veilig maakt: een verkoop die zonder verbinding wordt aangeslagen, blijft in de wachtrij staan en wordt verstuurd zodra de verbinding terugkomt.

Details die ertoe doen:

  • Schrijfacties op het winkelwagentje worden per bestelling geserialiseerd. Snelle reeksen scans worden één voor één toegepast, en een herhaalde toevoeging wordt samengevoegd met de regel die het dupliceert in plaats van dat er een tweede regel in de wachtrij komt.
  • Bevestigingen van bestellingen nemen de kopie van de server over. WooCommerce kent bij het aanmaken ID's toe aan de regelitems van een bestelling; de engine neemt de bevestigde bestelling over zodat latere updates op die regels aansluiten in plaats van dubbele regels toe te voegen. Die overname is behoudend — een lokale bewerking die de server nog niet heeft gezien wordt nooit overschreven, en het geldt alleen voor bestellingen.
  • Een schrijfactie die de server definitief weigert, wordt nooit stilzwijgend opnieuw geprobeerd. Die wordt geparkeerd met de reden van de server zelf en getoond op Winkelstatus → Database als „wijzigingen die je server nooit hebben bereikt”, met twee expliciete acties: Opnieuw versturen (bouwt het verzoek opnieuw op uit het record zoals het er nú uitziet, zodat latere correcties meegaan) en Verwerpen. Er is geen automatische herhaallus — herstel is altijd een zichtbare, bewuste handeling. Zie Winkelstatus voor de uitleg vanuit het perspectief van de verkoper.

Meerdere browsertabbladen

De web-POS in meerdere tabbladen van dezelfde winkel draaien wordt ondersteund. Elk tabblad kan een verkoop aanslaan — schrijfacties worden aan de gedeelde wachtrij toegevoegd — maar één gekozen tabblad doet het versturen voor elk bereik van winkel + kassamedewerker. Als dat tabblad sluit, promoveert de browser automatisch het volgende. Twee tabbladen die zijn ingelogd als verschillende kassamedewerkers zijn aparte bereiken en beheren elk hun eigen wachtrij.

Lokale opslag

De web- en desktop-app slaan gegevens op via een OPFS-worker (Origin Private File System); de iOS- en Android-apps gebruiken hetzelfde schijfformaat via een bestandssysteem-engine. Alle vier platforms delen één opslagformaat en dezelfde gereedschappen voor herstel na corruptie. (Eerdere versies gebruikten IndexedDB op het web en SQLite op native.)

Query's worden binnen de databaselaag uitgevoerd — selector, sortering en paginering — zodat alleen de zichtbare pagina met rijen naar de app gaat. Op een synthetische fixture met 10.000 bestellingen bracht die pushdown de kosten per schrijfactie onder een live abonnement terug van ~27 ms naar ~0,06 ms.

Upgraden vanaf v1.9

v1.10.0 migreert geen lokale databases — er wordt een koude hersynchronisatie uitgevoerd:

  1. Bij de eerste start opent de app een nieuwe lokale database en downloadt opnieuw vanuit je winkel. Reken op een eenmalige volledige herdownload op elk apparaat.
  2. Er gaat niets verloren: je WooCommerce-server is de gezaghebbende kopie van alle gesynchroniseerde gegevens. Openstaande, niet-verzonden wijzigingen worden verstuurd of getoond voordat oude gegevens worden opgeruimd — de upgrade kan geen niet-verzonden verkoop vernietigen.
  3. De app en de plug-in worden in lockstep uitgebracht. v1.10.0-clients spreken de v2-synchronisatie-API van de plug-in. Als maar één kant is geüpgraded, mislukken verzoeken met rest_no_route-fouten — die signatuur betekent „werk de andere helft bij”, niet dat de installatie kapot is.

Wat er is veranderd in v1.10.0

v1.9.xv1.10.0
Synchronisatie-eenheidEén replicatiestroom per aangekoppelde schermqueryEén engine per site + winkel + kassamedewerker
Wat een ophaalactie aanstuurtHet aankoppelen van een schermEen scherm dat declareert wat het nodig heeft
WijzigingsdetectiePeriodieke polls + volledige audit per uurCursor in het wijzigingslog met voorwaardelijke 304-antwoorden
ServertotalenNiet bijgehoudenTotalen per collectie met eerlijke controleren…-statussen
Mislukte schrijfactiesOndoorzichtig opnieuw geprobeerdDuurzame wachtrij, zichtbaar herstelpaneel, geen stille herhalingen
Web met meerdere tabbladenOngecoördineerdEén gekozen verzender per bereik
Lokaal zoekenOvereenkomst op woordbeginOvereenkomst op deeltekst (minimaal 3 tekens)
OpslagIndexedDB (web), SQLite (native)Opslag in OPFS-formaat op alle platforms
Synchronisatie afstemmenVastVoorinstellingen en schuifregelaars per apparaat in Winkelstatus

Voor de prestatiefilosofie achter de engine — maxima voor verzoeken, terugschakelen bij serverdruk en de gemeten cijfers — zie Synchronisatieprestaties.