# Java concurrent queue for tag change scripts

**URL:** <https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044>\
**Category:** Ignition\
**Created:** [November 17, 2023, 1:53pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044 "2023-11-17T13:53:19Z")\
**Posts on this page:** 14\
**Page:** 4

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [December 10, 2024, 1:41pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/61 "2024-12-10T13:41:38Z")

</div>

Did you read the linked topic in the first post?

Some more about this:

> [@Best practices and pitfalls with Tag Event Scrips](https://forum.inductiveautomation.com/t/best-practices-and-pitfalls-with-tag-event-scrips/99355):
>
> In summary, my question is: What is best practices, and what to avoid when writing Tag Event Scripts? Background and more details regarding question: From what I have read, the Tag Event Scripts are running in fixed size thread pool (default 3 threads). This would mean that if you have multiple Tag Event Scripts that does slow/blocking operations, it could block any other tag event scripts from running. This would presumably mean that running e.g. network calls like OPC calls, or other poten…

`system.util.invokeAsynchronous()` is a very heavy-weight tool and should not be used where you could make hundreds or thousands of threads.

---

<div class="post-metadata">

**Author:** ![louagejasper](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/louagejasper/32/86311_2.png) [@louagejasper](https://forum.inductiveautomation.com/u/louagejasper)\
**Post date:** [December 10, 2024, 2:01pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/62 "2024-12-10T14:01:23Z")

</div>

Oh, I looked over that link in the first post. That clarifies the use case allot, thanks!

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [December 12, 2024, 6:00am UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/63 "2024-12-12T06:00:02Z")

</div>

> [@pturmel](#):
>
> Ok. Updated in place: [queueUtil.py](https://www.automation-pros.com/ignition/queueUtil.py)

Hey @pturmel, how does this work with redundant gateways? What will happen to enqueued items if the redundant gateway kicks in? I assume these are dropped if the GW goes down. How would you make it more tolerant?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [December 12, 2024, 1:20pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/64 "2024-12-12T13:20:46Z")

</div>

Dropped on failover.

You will need a bunch of state management in your queue draining routine to detect when it is newly "live" after fail-over, and run discovery/recovery code.

Ignition redundancy isn't really designed for seamless state hand-off in any case.

---

<div class="post-metadata">

**Author:** ![louagejasper](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/louagejasper/32/86311_2.png) [@louagejasper](https://forum.inductiveautomation.com/u/louagejasper)\
**Post date:** [December 20, 2024, 10:13am UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/65 "2024-12-20T10:13:42Z")

</div>

Another question that comes to mind:

In an expression tag, is using runScript on a timer subject to the same vulnerability? Or is that an entirely different system in Ignition?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [December 20, 2024, 1:28pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/66 "2024-12-20T13:28:42Z")

</div>

> [@louagejasper](#):
>
> subject to the same vulnerability?

Yes, it is a separate thread pool for expressions, but is also size three. Scripts won't block expressions and vice versa, but long-running expressions can block all tag expression execution.

---

<div class="post-metadata">

**Author:** ![nicholas.gutt](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nicholas.gutt/32/25670_2.png) [@nicholas.gutt](https://forum.inductiveautomation.com/u/nicholas.gutt)\
**Post date:** [January 9, 2025, 7:50pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/67 "2025-01-09T19:50:22Z")

</div>

I have been trying to get queueUtil to work, failing hard.... ☹

I am confused maybe someone, pturmel, or nminchin explain:

what exactly goes into the tagChange script?  
what exactly goes into the Gateway Event Timer script?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [January 9, 2025, 9:06pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/68 "2025-01-09T21:06:11Z")

</div>

Did you read the multiline comment string between the copyright and the imports? Examples right there.

Let me summarize:

- In those examples, replace "ABC" with some reasonable name for what you are doing.

- Place `queueUtil.py` into a library script named `queueUtil` in an inheritable project that will be included in both your gateway scripting project and whatever project will run the timer events.

- In the gateway scripting project, create a library script to contain the assignment to `processABCqueue` and the function definition `enqueueABC`.

- In every tag (or UDT) that need to run this process. make a value change event with the one-liner `someScriptGSP.enqueueABC(...)` as described in the comment above that example.

- In the background events project, create a library script to contain the assignment to `processABCqueue` and the function definition `processABC`, including the decorator.

- In one or more (if workload justifies) timer events in the background project, make one-liner calls to `someScriptBG.processABC()`. (No arguments.)

Also, make sure my Integration Toolkit is installed.

---

<div class="post-metadata">

**Author:** ![nicholas.gutt](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nicholas.gutt/32/25670_2.png) [@nicholas.gutt](https://forum.inductiveautomation.com/u/nicholas.gutt)\
**Post date:** [January 9, 2025, 10:27pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/69 "2025-01-09T22:27:25Z")

</div>

I have the Integration Toolkit installed.

I put queueUtil in my gateway scripting project, currently gateway inheritance is a single project. Not a structure like your docstring calls out.

`processABCqueue` and the function definition `enqueueABC`.  
I did not understand needed to be in a separate library project.

I unfortunately will not be able to do anymore ignition work until next week, going on vacation.

Ill report back when I am successful.

Thank you! Your advice is very valuable.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [January 9, 2025, 10:55pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/70 "2025-01-09T22:55:46Z")

</div>

Ok, Can be one script in a single project. The docstring calls out the requirements based on split duties for different kinds of projects.

---

<div class="post-metadata">

**Author:** ![nicholas.gutt](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nicholas.gutt/32/25670_2.png) [@nicholas.gutt](https://forum.inductiveautomation.com/u/nicholas.gutt)\
**Post date:** [January 14, 2025, 6:36pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/71 "2025-01-14T18:36:56Z")

</div>

> [@pturmel](#):
>
> In the background events project, create a library script to contain the assignment to `processABCqueue` and the function definition `processABC`, including the decorator.

I got queueUtil working! Thank you again for providing the community with your time, software, and expertise. I have solved many problems using your advice.

---

<div class="post-metadata">

**Author:** ![nicholas.gutt](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nicholas.gutt/32/25670_2.png) [@nicholas.gutt](https://forum.inductiveautomation.com/u/nicholas.gutt)\
**Post date:** [January 14, 2026, 9:51pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/72 "2026-01-14T21:51:15Z")

</div>

I am back to building/testing with queueUtil. Everything is working well, and I have some questions.

Our production system contains 4 gateways. Each gateway has 40-300 assets connected. Each asset has a UDT instance recording machine signals (status, count, cycle) to SQL. Currently the system is using Gateway Events tag change with manually managed tag lists.  
I would love to replace the system with queueUtil, and am working to do so.

My questions:

Should I create a queue for each individual tag path inside a UDT instance? or use one queue with a single drain branching based on the tagPath?

Does anyone have recommendations on load testing?  
I have a testing gateway with 100 UDT instances (assets) changing much faster than real assets do. I am using Ignitions simulator. The simulator is setup to provide predictable signals so when I query the resulting data, I can validate tag changes recorded are consistent and accurate. I am using a single queue of size 750, and a single drain. Currently the test system is writing around 36,000 changes per hour and I have not seen any dropped enqueues. I am currently using a single drain timer running 1000ms and the longest I have seen the drain has take is 800ms.

What is the downside to increasing the size of the queue but not needing it?

On test setup, I started with 4 UDT instances, then increased to 20, now at 100. I have increased the size of the queue with the number of UDT instances. I have not seen any noticeable gateway performance change (RAM and CPU). Leaves me wondering how many UDT instances I need to create to make my testing gateway feel something. FWIW the testing gateway is identical to production gateways in terms of hardware/ram/cpu.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [January 14, 2026, 10:06pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/73 "2026-01-14T22:06:33Z")

</div>

I would use one queue until your algorithm becomes more complex. Switch on tagpath. Your current workload sounds impressive, but is only 10 changes per second. Even trivial hardware can keep up with that. I'd be worried when the workload hits hundreds or maybe thousands of changes per second.

Your database is going to be your bottleneck. When you hit that point, you may need to run multiple drain timers (for multiple threads) and have each gather multiple inserts into a single DB call. Most DBs will perform dramatically better when many inserts are delivered in one JDBC round trip.

---

<div class="post-metadata">

**Author:** ![nicholas.gutt](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nicholas.gutt/32/25670_2.png) [@nicholas.gutt](https://forum.inductiveautomation.com/u/nicholas.gutt)\
**Post date:** [January 15, 2026, 2:14pm UTC](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044/74 "2026-01-15T14:14:46Z")

</div>

Your points are taken and understood, thank you. Testing is using a completely separate server/database with significantly less traffic than the production system.

[Previous page](https://forum.inductiveautomation.com/t/java-concurrent-queue-for-tag-change-scripts/82044.md?page=3)
