Sharing a single memory tag, but prevent race conditions from multiple people potentially using it

We have created a custom internal ticketing/work order system that’s coming along great. One concept I’m trying to make smoother is sending out an email through our Alarm Pipeline when a new ticket is created.

I have all the logic and steps working, but I’m trying to prevent a race condition if multiple people were to create one at the same time. I have created a way through system.net.sendEmail that works since it’s done in the client itself using system.util.invokeAsynchronous to prevent client lock up, but wanted to utilize our already setup Alarm Pipeline with Associated Data groups which gives us some more dynamics.

What I did was setup a memory tag with 4 alarms on it (low, medium, high, critical), with each set to trigger when equal to 1, 2, 3, 4 respectively. What I also did was create other memory tags which hold strings that the Alarms are binded to for things such as subject, message, assoc. data. (Note I could do one dataset tag with everything, but the same concept).

When a new ticket is created, a button will do all the necessary things, and then write the priority number to the memory tag (which triggers one of the 4 priority alarms on it), which then has a tag event script that simply changes it back to 0. With the Alarm Pipeline’s Dropout Conditions for acknowledge and cleared off, the alarm runs fine. I can create tickets one after another seconds apart. However, if done fast enough or at the same time, the memory tags that hold string data for the alarm’s binded subject, body, etc… from the previous tag might still be go into a ticket after it.

I created a while loop that does a check to match everything up and an invokeLater, which improved it, but I would still like to foolproof it a little more. I though of the system.tag.configure to write the alarm’s data directly, but I believe that still could cause a race condition. Any suggestions?

Why not build a dataset tag that acts as a queue and have your tickets append to that queue.

Then you can use a timer tag to act as the dispatcher. when the dispatcher tag updates (with whatever scan class you assign it to) have it check the dataset tag to see if it has any new records and then check your memory tags to see if they’re available to be written to. If they are, simply take a record out of the queue and write it to the respective memory tags

A queue makes sense. What information of a ticket would go in there, just the id and info to be written to the memory tags for the alarms? We have SQL server database with about 20 columns so far that gets written per ticket (might grow). Also, how would we tell if the memory tags are available to be written to? Is there a way to ‘mark’ it or put a hold on it?

Thanks

If you already have a database with all of this info in it, then you could set up a query tag that pulls the “next unsent ticket” and then alarm on change

Then in the alarm pipeline after it’s sent, mark the record as “emailed” in the database

That way once it marks the ticket as emailed, the query tag will change to evaluate the next ticket

Thanks for the replies, I ended up solving using a simple method that I didn’t need to add much to what I had. It kind of combines both of the ideas given by creating a queue tag and using the script block in the Alarm Pipeline.

*Just to note, since I’m using a sql database, it already has it’s way of preventing the same id, so there was no issue writing new tickets to it, just in Ignition’s tags.

In the submit button script on the new ticket form, towards the end, I read the queueTag for a 0 or 1. I have a while loop that checks the queueTag and if it is a 0, it will evaluate to false and break (meaning nothing else has a “lock” on it). If it is a 1 (or not a 0), it means another ticket was created just before it going through the process and has not ended. it will continue to loop through until it is a 0.
After the while loop, it then writes a 1 to the “queueTag” which “locks” the tag, then writes to the memory tags, then goes though the alarm pipeline, which has a script block that simply writes a 0 to the queueTag.

Press Submit > do a bunch of script stuff > read queueTag for 0(free) or 1(lock) > while loop != 0, read queueTag inside, breaks when 0 > writes 1 for itself to queueTag to “lock” > writes memory tags > triggers alarmTicket tag which has an event script which reads memory tags > Alarm Pipeline uses a script block to unlock the queueTag by writing a 0 back to it >>> Repeat.

That will be abominably unreliable. Tags simply are not suited to locking.

Have the clients use system.util.sendRequest() and move the actual logic to a script module called from the message handler in the gateway. Use a top level python lock object in a with clause to protect your critical section.

Thank you for the tips, I’m familiar with the system.util.sendRequest(), but will need to read up on the python lock object and with clause.

Just curious, what reasons and examples do you consider tags being poor choice for locking?

Your loop while not zero followed by a write is prone to race conditions. Multiple processes/threads/clients/whatever could see the zero then write their own one.

You need an atomic compare-and-swap operation to implement this kind of thing safely and tags simply don't have that.

1 Like