Optimistische vergrendeling voor admin-tools: voorkom stille overschrijvingen
Leer optimistische vergrendeling voor admin-tools met versiekolommen en updated_at-controles, plus eenvoudige UI-patronen om bewerkingsconflicten af te handelen zonder stille overschrijvingen.

Het probleem: stille overschrijvingen wanneer veel mensen bewerken
Een “stille overschrijving” ontstaat wanneer twee mensen hetzelfde record openen, allebei wijzigingen aanbrengen, en de laatste die op Opslaan klikt wint. De bewerkingen van de eerste persoon verdwijnen zonder waarschuwing en vaak zonder eenvoudige manier om ze terug te halen.
In een druk admin-paneel gebeurt dit de hele dag zonder dat iemand het merkt. Mensen houden meerdere tabbladen open, springen tussen tickets en komen terug bij een formulier dat 20 minuten heeft liggen wachten. Wanneer ze uiteindelijk opslaan, werken ze niet met de nieuwste versie van het record. Ze overschrijven die.
Dit komt vaker voor in backoffice-tools dan in publieke apps omdat het werk collaboratief en record-gebaseerd is. Interne teams bewerken dezelfde klanten, bestellingen, producten en verzoeken herhaaldelijk, vaak in korte bursts. Publieke apps zijn vaker “één gebruiker bewerkt zijn eigen dingen”, terwijl admin-tools “veel gebruikers bewerken gedeelde dingen” zijn.
De schade is zelden dramatisch op het moment zelf, maar het telt snel op:
- Een productprijs wordt teruggezet naar een oude waarde vlak na een promotie-update.
- Een interne notitie van een supportmedewerker verdwijnt, waardoor de volgende medewerker hetzelfde probleem oplost.
- Een orderstatus springt terug (bijv. van “Verzonden” naar “Gepakt”) en triggert de verkeerde vervolgstap.
- Een telefoonnummer of adres van een klant wordt vervangen door verouderde info.
Stille overschrijvingen zijn pijnlijk omdat iedereen denkt dat het systeem correct heeft opgeslagen. Er is geen duidelijk “er ging iets mis”-moment, alleen verwarring later wanneer rapporten niet kloppen of een collega vraagt: “Wie heeft dit veranderd?”
Conflicten als deze zijn normaal. Ze zijn een teken dat het hulpmiddel gedeeld en nuttig is, niet dat je team iets fout doet. Het doel is niet om te voorkomen dat twee mensen bewerken. Het is om te detecteren dat het record veranderd is terwijl iemand aan het bewerken was, en dat moment goed af te handelen.
Als je een intern hulpmiddel bouwt in een no-code platform zoals AppMaster, is het de moeite waard dit vroeg te plannen. Admin-tools groeien snel en zodra teams erop vertrouwen, verandert “soms data verliezen” in een constante bron van wantrouwen.
Optimistische vergrendeling in gewone taal
Wanneer twee mensen hetzelfde record openen en allebei op Opslaan klikken, heb je concurrency. Iedere persoon begon met een oudere snapshot, maar slechts één kan uiteindelijk de “laatste” zijn wanneer de saves plaatsvinden.
Zonder bescherming wint de laatste save. Zo ontstaan stille overschrijvingen: de tweede save vervangt stilletjes de wijzigingen van de eerste persoon.
Optimistische vergrendeling is een simpele regel: “Ik sla mijn wijzigingen alleen op als het record nog steeds in dezelfde staat is als toen ik begon met bewerken.” Als het record tussentijds veranderde, wordt de save afgewezen en ziet de gebruiker een conflict.
Dit verschilt van pessimistische vergrendeling, wat meer is als “ik ben dit aan het bewerken, dus niemand anders mag.” Pessimistische vergrendeling betekent meestal harde locks, time-outs en geblokkeerde gebruikers. Het kan nuttig zijn in zeldzame gevallen (zoals geld overmaken tussen rekeningen), maar het is vaak frustrerend in drukke admin-tools waar de hele dag veel kleine bewerkingen plaatsvinden.
Optimistische vergrendeling is meestal de betere standaard omdat het de workflow laat doorlopen. Mensen kunnen parallel werken en het systeem grijpt alleen in bij een echte botsing.
Het past het beste wanneer:
- Conflicten mogelijk zijn maar niet constant.
- Bewerking snel zijn (enkele velden, een kort formulier).
- Anderen blokkeren het team zou vertragen.
- Je een duidelijke “iemand heeft dit bijgewerkt”-melding kunt tonen.
- Je API een versie (of timestamp) bij elke update kan controleren.
Wat het voorkomt is het probleem van de “stille overschrijving”. In plaats van dataverlies krijg je een duidelijke stop: “Dit record is veranderd sinds je het opende.”
Wat het niet kan doen is ook belangrijk. Het voorkomt niet dat twee mensen verschillende (maar valide) beslissingen nemen op basis van dezelfde verouderde info, en het voegt geen automatische merge toe. En als je de check niet op de server doet, heb je het probleem niet echt opgelost.
Veelvoorkomende beperkingen om in gedachten te houden:
- Het lost conflicten niet automatisch op (er is nog steeds een keuze nodig).
- Het helpt niet als gebruikers offline bewerken en later syncen zonder checks.
- Het verhelpt slechte permissies niet (iemand kan nog steeds bewerken wat hij niet zou mogen).
- Het vangt conflicten niet als je alleen in de client controleert.
In de praktijk is optimistische vergrendeling gewoon één extra waarde die met de bewerking meegaat, plus een serverzijde regel “update alleen als het matcht”. Als je een admin-paneel in AppMaster bouwt, leeft die check meestal in je businesslogica precies waar updates worden uitgevoerd.
Twee veelgebruikte benaderingen: versiekolom vs updated_at
Om te detecteren dat een record veranderde terwijl iemand het bewerkte, kies je meestal één van twee signalen: een versienummer of een updated_at-timestamp.
Benadering 1: Versiekolom (oplopend integer)
Voeg een version-veld toe (meestal een integer). Wanneer je het bewerkingsformulier laadt, laad je ook de huidige version. Bij het opslaan stuur je diezelfde waarde terug.
De update slaagt alleen als de opgeslagen versie nog steeds overeenkomt met wat de gebruiker had. Als het overeenkomt, werk je het record bij en verhoog je version met 1. Als het niet overeenkomt, geef je een conflict terug in plaats van te overschrijven.
Dit is makkelijk te begrijpen: versie 12 betekent “dit is de 12e wijziging.” Het vermijdt ook tijd-gerelateerde randgevallen.
Benadering 2: updated_at (timestampvergelijking)
De meeste tabellen hebben al een updated_at-veld. Het idee is hetzelfde: lees updated_at wanneer het formulier opent en stuur het mee met de save. De server update alleen als updated_at ongewijzigd is.
Het kan goed werken, maar timestamps hebben valkuilen. Verschillende databases slaan verschillende precisie op. Sommige ronden af op seconden, wat snelle bewerkingen kan missen. Als meerdere systemen naar dezelfde database schrijven, kunnen klokafwijkingen en tijdzoneafhandeling ook verwarrende randgevallen opleveren.
Een eenvoudige vergelijking:
- Versiekolom: heldere gedragingen, draagbaar tussen databases, geen klokproblemen.
updated_at: vaak “gratis” omdat het al bestaat, maar precisie en klokafhandeling kunnen problemen geven.
Voor de meeste teams is een versiekolom de betere primaire indicator. Het is expliciet, voorspelbaar en makkelijk te refereren in logs en supporttickets.
Als je in AppMaster bouwt, betekent dit doorgaans dat je een integer version-veld toevoegt in de Data Designer en ervoor zorgt dat je update-logica het controleert voordat het opslaat. Je kunt updated_at blijven gebruiken voor auditing, maar laat het versienummer bepalen of een bewerking veilig toegepast kan worden.
Wat op te slaan en wat mee te sturen bij elke bewerking
Optimistische vergrendeling werkt alleen als elke bewerking een “laatst gezien”-marker meedraagt van het moment dat de gebruiker het formulier opende. Die marker kan een version-nummer of een updated_at-timestamp zijn. Zonder die marker kan de server niet weten of het record veranderd is terwijl de gebruiker typte.
Op het record zelf houd je je normale businessvelden, plus één concurrencytoken die de server beheert. De minimale set ziet er zo uit:
id(stabiele identifier)- businessvelden (naam, status, prijs, notities, enz.)
version(integer die bij elke succesvolle update verhoogt) ofupdated_at(server-geschreven timestamp)
Wanneer het bewerkscherm laadt, moet het formulier de laatst-gezien waarde van dat concurrencerveld bewaren. Gebruikers mogen het niet aanpassen, dus bewaar het als verborgen veld of in de formulierstate. Voorbeeld: de API retourneert version: 12 en het formulier houdt 12 totdat er op Opslaan geklikt wordt.
Wanneer de gebruiker op Opslaan klikt, stuur je twee dingen: de wijzigingen en de laatst-gezien marker. De eenvoudigste vorm is om het in de update-request body op te nemen, zoals id, gewijzigde velden en expected_version (of expected_updated_at). Als je de UI in AppMaster bouwt, behandel dit als elke andere gebonden waarde: laad het met het record, houd het ongewijzigd en dien het samen met de update in.
Op de server moet de update conditioneel zijn. Je update alleen als de verwachte marker overeenkomt met wat momenteel in de database staat. Merge niet stilletjes.
Een conflictresponse moet duidelijk en eenvoudig te verwerken zijn in de UI. Een praktische conflictresponse bevat:
- HTTP-status
409 Conflict - een korte boodschap zoals “Dit record is door iemand anders bijgewerkt.”
- de huidige serverwaarde (
current_versionofcurrent_updated_at) - optioneel het huidige serverrecord (zodat de UI kan tonen wat er veranderde)
Voorbeeld: Sam opent een Klantrecord op versie 12. Priya slaat een wijziging op, waardoor het versie 13 wordt. Sam klikt later op Opslaan met expected_version: 12. De server retourneert 409 met het huidige record op versie 13. Nu kan de UI Sam vragen de nieuwste waarden te bekijken in plaats van Priya’s wijziging te overschrijven.
Stapsgewijs: optimistische vergrendeling end-to-end implementeren
Optimistische vergrendeling komt neer op één regel: iedere bewerking moet bewijzen dat deze gebaseerd is op de laatst opgeslagen versie van het record.
1) Voeg een concurrencerveld toe
Kies één veld dat bij elke write verandert.
Een dedicated integer version is het makkelijkst. Begin bij 1 en verhoog bij elke update. Als je al een betrouwbare updated_at-timestamp hebt die altijd verandert, kun je die gebruiken, maar zorg dat hij op elke write wordt bijgewerkt (ook door achtergrondjobs).
2) Stuur die waarde naar de client bij lezen
Wanneer de UI een bewerkscherm opent, neem de huidige version (of updated_at) op in de response. Sla het op in de formulierstate als verborgen waarde.
Zie het als een ontvangstbewijs dat zegt: “Ik bewerk wat ik laatst heb gelezen.”
3) Vereis die waarde bij update
Bij Opslaan stuurt de client de bewerkte velden plus de laatst-gezien concurrencetoken.
Op de server maak je de update conditioneel. In SQL-termen is het:
UPDATE tickets
SET status = $1,
version = version + 1
WHERE id = $2
AND version = $3;
Als de update 1 rij raakt, is de save gelukt. Als het 0 rijen zijn, heeft iemand anders het record al veranderd.
4) Geef de nieuwe waarde terug na succes
Na een succesvolle save retourneer je het bijgewerkte record met de nieuwe version (of nieuwe updated_at). De client moet de formulierstate vervangen door wat de server teruggeeft. Dit voorkomt “dubbele saves” met een oude versie.
5) Behandel conflicten als normale uitkomst
Wanneer de conditionele update faalt, geef een duidelijke conflictresponse (vaak HTTP 409) die omvat:
- het huidige record zoals het er nu uitziet
- de poging van de client (of genoeg info om het te reconstrueren)
- welke velden verschillen (als je dat kunt berekenen)
In AppMaster past dit goed bij een PostgreSQL-modelveld in de Data Designer, een read-endpoint dat de versie teruggeeft, en een Business Process dat de conditionele update uitvoert en aftakt naar succes of conflict.
UI-patronen die conflicten afhandelen zonder gebruikers te irriteren
Optimistische vergrendeling is maar de helft van het verhaal. De andere helft is wat de gebruiker ziet wanneer zijn save wordt geweigerd omdat iemand anders het record wijzigde.
Goede conflict-UI heeft twee doelen: stop stille overschrijvingen en help de gebruiker snel zijn taak af te maken. Goed uitgevoerd voelt het als een nuttige veiligheidsband, niet als een blokkade.
Patroon 1: De eenvoudige blokkerende dialoog (snelste)
Gebruik dit wanneer bewerkingen klein zijn en gebruikers hun wijziging veilig opnieuw kunnen toepassen na herladen.
Houd de boodschap kort en specifiek: “Dit record veranderde terwijl je bewerkte. Herlaad om de nieuwste versie te zien.” Geef vervolgens twee duidelijke acties:
- Herlaad en doorgaan (primaire)
- Kopieer mijn wijzigingen (optioneel maar handig)
“Kopieer mijn wijzigingen” kan de ongeslagen waarden naar het klembord zetten of ze in het formulier bewaren na herladen, zodat mensen niet hoeven te onthouden wat ze typten.
Dit werkt goed voor single-field updates, toggles, statuswijzigingen of korte notities. Het is ook het makkelijkst te implementeren in de meeste builders, inclusief AppMaster-gebaseerde admin-schermen.
Patroon 2: “Bekijk wijzigingen” (best voor waardevollere records)
Gebruik dit wanneer het record belangrijk is (prijzen, permissies, uitbetalingen, accountinstellingen) of het formulier lang is. In plaats van een doodlopende fout, leid je de gebruiker naar een conflictscherm dat vergelijkt:
- “Jouw bewerkingen” (wat ze probeerden op te slaan)
- “Huidige waarden” (laatste uit de database)
- “Wat veranderde sinds je het opende” (de conflicterende velden)
Houd het gefocust. Toon niet elk veld als er maar drie velden in conflict zijn.
Voor elk conflicterend veld bied eenvoudige keuzes:
- Houd de mijne
- Neem die van hen
- Merge (alleen wanneer logisch, zoals tags of notities)
Nadat de gebruiker conflicten heeft opgelost, sla je opnieuw op met de nieuwste versiewaarde. Als je rich text of lange notities ondersteunt, toon dan een kleine diff-weergave (toegevoegd/verwijderd) zodat gebruikers snel kunnen beslissen.
Wanneer een geforceerde overschrijving toestaan (en door wie)
Soms is een geforceerde overschrijving nodig, maar het moet zeldzaam en gecontroleerd zijn. Als je het toevoegt, maak het dan bewust: vraag om een korte reden, log wie het deed en beperk het tot rollen zoals admins of supervisors.
Voor reguliere gebruikers is de default “Bekijk wijzigingen.” Geforceerd overschrijven is het meest verdedigbaar wanneer de gebruiker eigenaar is van het record, het record laag-risico is, of het systeem onder toezicht slechte data corrigeert.
Voorbeeldscenario: twee collega’s bewerken hetzelfde record
Twee supportmedewerkers, Maya en Jordan, werken in hetzelfde admin-hulpmiddel. Ze openen allebei hetzelfde klantprofiel om de klantstatus bij te werken en notities toe te voegen na afzonderlijke gesprekken.
Tijdlijn (met optimistische vergrendeling ingeschakeld via version of updated_at-controle):
- 10:02 - Maya opent Klant #4821. Het formulier laadt Status = "Needs follow-up", Notities = "Called yesterday" en Version = 7.
- 10:03 - Jordan opent dezelfde klant. Hij ziet dezelfde data, ook Version = 7.
- 10:05 - Maya zet Status op "Resolved" en voegt een notitie toe: "Issue fixed, confirmed by customer." Ze klikt op Opslaan.
- 10:05 - De server werkt het record bij, verhoogt Version naar 8 (of werkt
updated_atbij) en slaat een audit-entry op: wie wat en wanneer wijzigde. - 10:09 - Jordan typt een andere notitie: "Customer asked for a receipt" en klikt op Opslaan.
Zonder concurrencyecheck kan Jordans save Maya’s status en notitie stilletjes overschrijven, afhankelijk van hoe de update is opgebouwd. Met optimistische vergrendeling wijst de server Jordans update af omdat hij probeert op te slaan met Version = 7 terwijl het record al Version = 8 is.
Jordan ziet een duidelijke conflictmelding. De UI toont wat er gebeurde en geeft hem een veilige volgende stap:
- Herlaad het nieuwste record (mijn bewerkingen weggooien)
- Pas mijn wijzigingen toe bovenop het nieuwste record (aanbevolen waar mogelijk)
- Bekijk verschillen ("Mijne" vs "Laatste") en kies wat te behouden
Een eenvoudig scherm kan tonen:
- “Deze klant werd bijgewerkt door Maya om 10:05”
- De velden die veranderden (Status en Notities)
- Een preview van Jordans ongeslagen notitie, zodat hij die kan kopiëren of opnieuw toepassen
Jordan kiest “Bekijk verschillen”, behoudt Maya’s Status = "Resolved" en voegt zijn notitie toe aan de bestaande notities. Hij slaat opnieuw op, dit keer met Version = 8, en de update slaagt (nu Version = 9).
Eindresultaat: geen dataverlies, geen giswerk over wie wat overschreef, en een nette audittrail die Maya’s statuswijziging en beide notities als aparte, traceerbare wijzigingen laat zien. In een tool gebouwd met AppMaster past dit netjes bij één check op update plus een klein conflict-resolutie-dialoog in de admin-UI.
Veelgemaakte fouten die nog steeds dataverlies veroorzaken
De meeste “optimistische vergrendeling”-bugs gaan niet over het idee, maar over de overdracht tussen UI, API en database. Als één laag de regels vergeet, kun je nog steeds stille overschrijvingen krijgen.
Een klassieker is het ophalen van de versie (of timestamp) wanneer het bewerkscherm laadt, maar het niet terugsturen bij opslaan. Dit gebeurt vaak wanneer een formulier hergebruikt wordt over pagina’s en het verborgen veld wegvalt, of wanneer een API-client alleen "gewijzigde" velden stuurt.
Een andere valkuil is conflictoplossing alleen in de browser. De gebruiker ziet misschien een waarschuwing, maar als de server de update toch accepteert, kan een andere client (of retry) data overschrijven. De server moet de definitieve poortwachter zijn.
Patronen die het meeste dataverlies veroorzaken:
- De concurrencetoken ontbreekt in het save-request (
version,updated_atof ETag), dus de server heeft niets om te vergelijken. - Updates accepteren zonder atomische conditie, zoals updaten op id alleen in plaats van "id + version".
updated_atgebruiken met lage precisie (bijv. seconden). Twee bewerkingen in dezelfde seconde kunnen identiek lijken.- Grote velden (notities, omschrijvingen) of hele arrays (tags, line items) vervangen zonder te tonen wat veranderde.
- Elk conflict behandelen als “gewoon opnieuw proberen,” wat verouderde waarden opnieuw kan toepassen bovenop nieuwere data.
Een concreet voorbeeld: twee teamleads openen hetzelfde klantrecord. De een voegt een lange interne notitie toe, de ander verandert de status en slaat op. Als jouw save de volledige recordpayload overschrijft, kan de statuswijziging per ongeluk de notitie wissen.
Wanneer een conflict gebeurt, verliezen teams nog steeds data als de API-response te weinig info bevat. Geef niet alleen “409 Conflict.” Geef genoeg voor een mens om het op te lossen:
- De huidige serverversie (of
updated_at) - De nieuwste serverwaarden voor de betrokken velden
- Een lijst met velden die verschillen (ook simpele namen volstaan)
- Wie het veranderde en wanneer (als je dat bijhoudt)
Als je dit in AppMaster implementeert, volg dezelfde discipline: houd de versie in de UI-state, stuur hem mee met de update en handhaaf de check in de backendlogica voordat je naar PostgreSQL schrijft.
Snelcontroles voordat je live gaat
Voer voor je rollout een korte controle uit op faalmodi die zorgen voor “het heeft goed opgeslagen” terwijl iemand anders werk stilletjes wordt overschreven.
Data- en API-checks
Zorg dat het record een concurrencetoken end-to-end draagt. Die token kan version (integer) of updated_at (timestamp) zijn, maar hij moet behandeld worden als onderdeel van het record, niet als optionele metadata.
- Reads bevatten de token (en de UI slaat hem op in de formulierstate, niet alleen op het scherm).
- Elke update stuurt de laatst-gezien token terug en de server verifieert deze voordat hij schrijft.
- Bij succes geeft de server de nieuwe token terug zodat de UI synchroon blijft.
- Bulk-bewerkingen en inline-bewerkingen volgen dezelfde regel, geen speciale short-cuts.
- Achtergrondjobs die dezelfde rijen bewerken controleren ook de token (anders ontstaan willekeurige conflicten).
Als je in AppMaster bouwt, controleer dan of het Data Designer-veld bestaat (version of updated_at) en dat je Business Process de waarde vergelijkt voordat de daadwerkelijke update wordt uitgevoerd.
UI-checks
Een conflict is alleen “veilig” als de volgende stap duidelijk is.
Wanneer de server een update weigert, toon dan een duidelijke melding zoals: “Dit record veranderde sinds je het opende.” Bied vervolgens één veilige actie eerst: herlaad de nieuwste data. Indien mogelijk voeg een “herlaad en herpas” pad toe dat de ongeslagen invoer van de gebruiker bewaart en opnieuw toepast op het verfriste record, zodat een kleine wijziging geen hertype-klus wordt.
Als je team het echt nodig heeft, voeg dan een gecontroleerde “force save”-optie toe. Beperk deze op rol, bevestig het en log wie het geforceerd heeft en wat er veranderde. Dat houdt noodgevallen mogelijk zonder dat dataverlies de standaard wordt.
Volgende stappen: voeg vergrendeling toe aan één workflow en breid uit
Begin klein. Kies één admin-scherm waar mensen elkaar regelmatig in de weg zitten en voeg daar eerst optimistische vergrendeling toe. Hoge-collision-gebieden zijn meestal tickets, bestellingen, prijzen en voorraad. Als je conflicten veilig maakt op één druk scherm, zie je snel het patroon om elders te herhalen.
Kies van tevoren je standaardgedrag bij conflicten, want dat bepaalt zowel backendlogica als UI:
- Block-and-reload: stop de save, herlaad het nieuwste record en vraag de gebruiker zijn wijziging opnieuw toe te passen.
- Review-and-merge: toon “jouw wijzigingen” versus “laatste wijzigingen” en laat de gebruiker beslissen wat te behouden.
Block-and-reload is sneller om te bouwen en werkt goed wanneer bewerkingen kort zijn (statuswijzigingen, toewijzing, korte notities). Review-and-merge is de moeite waard bij lange of hoge-stakes records (prijstabellen, multi-veld orderbewerkingen).
Implementeer en test één compleet flow voordat je uitbreidt:
- Kies één scherm en noteer de velden die gebruikers het meest bewerken.
- Voeg een versie (of
updated_at) toe aan de formulierpayload en verplicht die bij opslaan. - Maak de update conditioneel in de database-write (alleen bij overeenkomstige versie).
- Ontwerp het conflictbericht en de volgende actie (herladen, tekst kopiëren, vergelijkingsweergave openen).
- Test met twee browsers: sla op in tab A, probeer dan verouderde data op te slaan in tab B.
Voeg lichte logging toe voor conflicten. Zelfs een simpele “conflict gebeurde” gebeurtenis met recordtype, schermnaam en gebruikersrol helpt je hotspots te vinden.
Als je admin-tools bouwt met AppMaster (appmaster.io), mappen de belangrijkste onderdelen netjes: modelleer een versieveld in de Data Designer, handhaaf conditionele updates in Business Processes en voeg een klein conflict-dialoog toe in de UI-builder. Zodra de eerste workflow stabiel is, herhaal je het patroon scherm voor scherm en houd je de conflict-UI consistent zodat gebruikers het één keer leren en overal vertrouwen hebben.
FAQ
Een stille overschrijving gebeurt wanneer twee mensen hetzelfde record bewerken vanaf verschillende tabs of sessies, en de laatste save de eerdere wijziging vervangt zonder waarschuwing. Het gevaar is dat beide gebruikers een “succesvolle save” zien, dus de ontbrekende wijzigingen pas later opgemerkt worden.
Optimistische vergrendeling betekent dat de app je wijziging alleen opslaat als het record niet veranderd is sinds je het opende. Als iemand anders eerder heeft opgeslagen, wordt jouw save geweigerd met een conflict zodat je de nieuwste data kunt bekijken in plaats van die te overschrijven.
Pessimistische vergrendeling blokkeert anderen om te bewerken terwijl jij bezig bent, wat vaak wachttijden, time-outs en “wie heeft dit vergrendeld?”-momenten veroorzaakt in admin-teams. Optimistische vergrendeling past meestal beter bij admin-panelen omdat mensen parallel kunnen werken en alleen bij echte botsingen hoeven te handelen.
Een versiekolom is meestal de eenvoudigste en meest voorspelbare optie omdat je dan geen last hebt van timestamp-precisie of klokproblemen. Een updated_at-controle kan werken, maar kan snelle bewerkingen missen als de timestamp weinig resolutie heeft of inconsistent wordt behandeld tussen systemen.
Je hebt een door de server beheerde concurrencytoken op het record nodig, meestal version (integer) of updated_at (timestamp). De client moet deze uitlezen wanneer het formulier opent, ongewijzigd houden tijdens het bewerken en terugsturen bij het opslaan als de verwachte waarde.
Omdat de client niet te vertrouwen is om gedeelde data te beschermen, moet de server de controle afdwingen met een conditionele update zoals “update where id matches and version matches”. Als de server dat niet doet, kan een andere client, retry of achtergrondtaak alsnog wijzigingen stilletjes overschrijven.
Een goed standaardantwoord is een blokkerend bericht: “Dit record veranderde sinds je het opende.” Bied vervolgens één veilige actie aan: laad de nieuwste data. Als iemand veel heeft getypt, bewaar dan hun ongeslagen invoer zodat ze die na herladen kunnen hergebruiken in plaats van opnieuw te moeten typen.
Geef een duidelijke conflictresponse (vaak 409) plus genoeg context om te herstellen: de huidige serverversie en de nieuwste serverwaarden. Als je kunt, voeg toe wie het heeft aangepast en wanneer, zodat de gebruiker begrijpt waarom de save geweigerd is en wat er veranderde.
Let op ontbrekende tokens bij het opslaan, updates die alleen op id filteren in plaats van id + version, en timestampcontroles met lage precisie. Ook gevaarlijk: hele recordpayloads overschrijven (lange notities, arrays) in plaats van alleen bedoelde velden bij te werken, want daarmee kun je iemands wijzigingen wissen.
In AppMaster voeg je een version-veld toe in de Data Designer en neem je het op in het record dat de UI in het formulier staat laadt. Handhaaf vervolgens een conditionele update in je Business Process zodat de write alleen slaagt als de verwachte versie overeenkomt, en behandel de conflict-branch in de UI met een reload-/review-flow.


