I have a ‘do when thing is created’ database trigger in my backend, and I’m noticing that sometimes it will run twice instead of just once which causes problems with our data integrity. Has anyone else noticed this happening? If so do you have any tips?
It’s definitely not something that happens frequently or predictably - in the example I found today there were a dozen things created more-or-less simultaneously and the database trigger workflow only ran twice on a couple of them. I’m wondering if the system can’t handle database changes that happen in too quick of succession without glitching.
Also just to clarify - the condition for the database trigger is ‘thing before change is empty’ and ‘thing after change is not empty’.
I encountered the same issue in one app and applied an ugly workaround: a server-side pause of 3-5 seconds that I added inside the trigger. It’s a private plugin we created, but it’s not the best solution.
The best approach would be to ensure that your trigger runs once or twice by creating custom logs. Consider creating a Log table with fields such as ‘workflow_name,’ etc. Inside the trigger, add an action to create a Log entry. This will help investigate the issue more effectively. If the issue is confirmed, please use these logs when contacting Bubble support.
Any chance you checked logs to see why it was triggered? Backend does tend to run out of sync at times and can be a messy fix. Maybe have the database trigger first step be to schedule another “checker” workflow first then proceed.
Many times when I have workflows doing this I break the process into multi step that all include a first step “checker” workflow and a last step “dataCleanUp” workflow to ensure everything stays in line.
Yes, checked the logs - both instances ran within a second of each other, and I didn’t see anything informative. Obviously the thing wasn’t created twice.
I did wonder briefly if the second instance was triggered by the first instance, since there’s a ‘make changes to thing’ action within the database trigger workflow. But as far as I know a database trigger can’t be kicked off by an action within another database trigger, so unless that’s changed sometime recently then I don’t think that would be the issue.
Now I’m wondering, though, if the thing isn’t considered fully ‘created’ until the database trigger workflow is finished? Because in that case any changes made to the database item immediately after creation (outside the database trigger workflow) might be made to a thing that doesn’t entirely exist yet… though I would think there’s logic inherent to the ‘make changes to result of step 1’ expression that would prevent something like that since that tends to force changes to happen sequentially rather than asynchronously.
while it’s not significant it’s still 0.2 wu for the plugin request + 1.5 or 2.5 wu just to do nothing and wait. So every time you make changes to a thing you also activate the database trigger that makes the ssa request and your simple making changes to a thing can be 6 times more expensive
Whenever I try to prove database triggers don’t work it works perfectly.
Whenever I rely on it to work for production apps they fail like 5% of the time. (Exaggeration may be included. ) I tested it again today - trying to see if it would work perfectly. It seems to.
Hey, so I figured out a solution to this. It’s a classic race condition - where the database trigger is invoked twice. The simple way to overcome it is to use a binary flag to indicate the process has begun. This will block the second invocation.
Just do this:
Create a number field in the relevant Thing called “IsProcessing”
Add a first step to your Database Trigger workflow of “Terminate Workflow” and the condition Thing Now’s IsProcessing is “1”
Then add as second step “Make Chagnes to Thing” and set IsProcessing as 1
This will invoke the trigger the first time, set the flag to 1, then when the second invocation happens, it’ll terminate the second workflow due to the IsProcessing flag.
This is a very common way to prevent race conditions happening in “normal” programming and seems to work here as well.