# Vision Client Request/sec too high

**URL:** <https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413>\
**Category:** Ignition\
**Tags:** ignition81, vision\
**Created:** [April 22, 2025, 4:26pm UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413 "2025-04-22T16:26:13Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![Leonel](https://avatars.discourse-cdn.com/v4/letter/l/4491bb/32.png) [@Leonel](https://forum.inductiveautomation.com/u/Leonel)\
**Post date:** [April 22, 2025, 4:26pm UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413/1 "2025-04-22T16:26:13Z")

</div>

Previously, I worked with only four clients, and the client response time was almost instantaneous for everything.  
The client asked me to add more stations; now we have 11 clients, and I've noticed some delay with clients.  
The client application is simple; it basically consists of screen changes so station operators can follow instructions (there are a lof of step for operators, so, i have a lot of screens). Among the 11 clients, I also control 15 Atlas tools using Open Protocol, and all 11 clients write data to a database simultaneously.

Things I think are wrong:

- I have a lot of system.tag.readBlocking and system.tag.writeBlocking on all clients. I've read that this isn't ideal.
- I have a client running on the gateway. Ideally, the gateway should be installed on a computer without clients.
- I don't have an SSD in the gateway. I don't know if installing one would improve it. Should I install SSDs on the clients as well?

Any other tips would be greatly appreciated. Thanks.

 ![Screenshot 2025-04-21 090616](https://us1.discourse-cdn.com/inductiveautomation/original/3X/a/a/aa1960622329f240bc6f2ae085cc6e6f8ca561a8.png)  
 ![Screenshot 2025-04-21 092159](https://us1.discourse-cdn.com/inductiveautomation/original/3X/2/5/25b15229587aa8bf6cd5a56a69c6845da7f1ef03.png)  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/b/1bde24f1ec9f55a4372d60a8ea95d4130937eef9.png)  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/8/4/84511d3069e1182cb39b78a46f1f9ec284fa64c2.png)

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 22, 2025, 4:35pm UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413/2 "2025-04-22T16:35:11Z")

</div>

> [@Leonel](#):
>
> I have a lot of system.tag.readBlocking and system.tag.writeBlocking on all clients. I've read that this isn't ideal.

Definitely not ideal, especially if you're calling it from the single GUI thread (the event dispatch thread or EDT). Blocking that thread is the most likely cause of "delay with clients". While 10000 requests/sec is _extremely_ high, it's not **necessarily** a problem on its own - your gateway seems to be dealing with it okay.

Is there a reason you arrived at "a lot of readBlocking and writeBlocking" calls over, say, bindings (bidirectional if necessary)? Bindings are vastly more efficient for reading - they set up subscriptions on the gateway so that only changed values are delivered, as well as running the IO work in a background thread and only delivering updated values on the EDT.  
If you _must_ use scripting, then at least ensure that you're batching reads and writes together into as few operations as possible. Ten `readBlocking` calls in a row in a script implies ten _sequential_ network hops to the gateway and back to receive a value.

---

<div class="post-metadata">

**Author:** ![Leonel](https://avatars.discourse-cdn.com/v4/letter/l/4491bb/32.png) [@Leonel](https://forum.inductiveautomation.com/u/Leonel)\
**Post date:** [April 22, 2025, 5:14pm UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413/3 "2025-04-22T17:14:16Z")

</div>

> [@paul-griffith](#):
>
> Definitely not ideal, especially if you're calling it from the single GUI thread (the event dispatch thread or EDT). Blocking that thread is the most likely cause of "delay with clients". While 10000 requests/sec is _extremely_ high, it's not **necessarily** a problem on its own - your gateway seems to be dealing with it okay.
> 
> Is there a reason you arrived at "a lot of readBlocking and writeBlocking" calls over, say, bindings (bidirectional if necessary)? Bindings are vastly more efficient for reading - they set up subscriptions on the gateway so that only changed values are delivered, as well as running the IO work in a background thread and only delivering updated values on the EDT.  
> If you _must_ use scripting, then at least ensure that you're batching reads and writes together into as few operations as possible. Ten `readBlocking` calls in a row in a script implies ten _sequential_ network hops to the gateway and back to receive a value.

Okay, understood, thanks!  
I think this is the script where ReadBlocking is most abused (it runs in one station every 4/5 minutes).  
I have four data arrays in the PLC, each with max 100 values. So I run a query to save those 400 data items to the database.  
Should I create 400 bindings?

```python
	if(pos2 > pos1):
		pos3 = pos2
	else:
		pos3 = pos1
	
	for i in range(pos3):

		GraphData1 = system.tag.readBlocking([("[Amvian]_PLC_/Program:TESTER/Graph_ST8/SlideTest_C1/FIFO_ARRAY_{}_".format(i))])[0].value
		GraphData2 = system.tag.readBlocking([("[Amvian]_PLC_/Program:TESTER/Graph_ST8/SlideTest_C2/TesterCurrent_{}_".format(i))])[0].value
		GraphData3 = system.tag.readBlocking([("[Amvian]_PLC_/Program:TESTER/Graph_ST8/SlidePos_C1/FIFO_ARRAY_{}_".format(i))])[0].value
		GraphData4 = system.tag.readBlocking([("[Amvian]_PLC_/Program:TESTER/Graph_ST8/SlidePos_C2/FIFO_ARRAY_{}_".format(i))])[0].value
		query = "INSERT INTO `amvian_db`.`graph_slidetest` (`Fecha`, `VIN`, `Model`, `C1`, `C2`, `Pos_C1`, `Pos_C2`) VALUES (?, ?, ?, ?, ?, ?, ?);"
		args = [date, VIN, model, GraphData1, GraphData2, GraphData3, GraphData4]
		system.db.runPrepUpdate(query, args)

```

---

<div class="post-metadata">

**Author:** ![HCampos](https://avatars.discourse-cdn.com/v4/letter/h/a88e57/32.png) [@HCampos](https://forum.inductiveautomation.com/u/HCampos)\
**Post date:** [April 22, 2025, 5:27pm UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413/4 "2025-04-22T17:27:46Z")

</div>

I used `system.tag.readBlocking` once and I had the same trouble. In my case I was using a OPC Server with the PLC and instead `tag.readBlocking` I used` system.opc.readValue()` and it worked better. I don't know if you are using a OPC too.  
I hope it could help.

---

<div class="post-metadata">

**Author:** ![ryan.white](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/ryan.white/32/73193_2.png) [@ryan.white](https://forum.inductiveautomation.com/u/ryan.white)\
**Post date:** [April 22, 2025, 5:32pm UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413/5 "2025-04-22T17:32:38Z")

</div>

Consolidate your read blocking calls into a single call as much as possible:

```python
		GraphData1, GraphData2, GraphData3, GraphData4 = [ 
			qv.value for qv in system.tag.readBlocking([
			"[Amvian]_PLC_/Program:TESTER/Graph_ST8/SlideTest_C1/FIFO_ARRAY_%d_" % i, 
			"[Amvian]_PLC_/Program:TESTER/Graph_ST8/SlideTest_C2/TesterCurrent_%d_" % i,
			"[Amvian]_PLC_/Program:TESTER/Graph_ST8/SlidePos_C1/FIFO_ARRAY_%d_" % i,
			"[Amvian]_PLC_/Program:TESTER/Graph_ST8/SlidePos_C2/FIFO_ARRAY_%d_" % i
		])
	]

```

You could go even further and use the for loop to build all the paths for that position, then read all the tags at once in a single `readBlocking` call and insert. 1 single jump to gateway and back instead of 4x however many positions you have.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 22, 2025, 6:39pm UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413/6 "2025-04-22T18:39:52Z")

</div>

`runPrepUpdate` is _also_ blocking.

The _easiest_ (but definitely not best) thing to do, if you just want the bandaid solution, is to wrap all this logic up into a function (preferably in the project library) and call it in an asynchronous thread (`system.util.invokeAsynchronous`). If you don't need any return value _wherever you're calling the script_, that's a pretty trivial change to make.

But you're leaving a _huge_ amount of optimization on the table. Assuming `pos3` is ~100 or whatever, you could issue a _single_ readBlocking call, assemble all your parameters, and issue a _single_ `runPrepUpdate` for all 100 rows in the DB at once too. That's probably going to run _more than 100x_ faster, because you're eliminating extra network hops _and_ making it easier on your database (bulk inserts make rebuilding indexes easier).

Maybe something like this (untested):

```python
if(pos2 > pos1):
	pos3 = pos2
else:
	pos3 = pos1

def paths(i):
	yield "[Amvian]_PLC_/Program:TESTER/Graph_ST8/SlideTest_C1/FIFO_ARRAY_{}_".format(i)
	yield "[Amvian]_PLC_/Program:TESTER/Graph_ST8/SlideTest_C2/TesterCurrent_{}_".format(i)
	yield "[Amvian]_PLC_/Program:TESTER/Graph_ST8/SlidePos_C1/FIFO_ARRAY_{}_".format(i)
	yield "[Amvian]_PLC_/Program:TESTER/Graph_ST8/SlidePos_C2/FIFO_ARRAY_{}_".format(i)

def chunks(lst, n):
    """Yield successive n-sized chunks from lst."""
    for i in xrange(0, len(lst), n):
        yield lst[i:i + n]

allPaths = list(paths(i) for i in xrange(pos3))
allValues = [qv.value for qv in system.tag.readBlocking(allPaths)]

chunked = chunks(allValues, 4)

query = """
	INSERT INTO `amvian_db`.`graph_slidetest` 
	(`Fecha`, `VIN`, `Model`, `C1`, `C2`, `Pos_C1`, `Pos_C2`)
	VALUES {}
	""".format(", ".join("(?, ?, ?, ?, ?, ?, ?)" for _ in xrange(len(chunked))))

baseArgs = [date, VIN, model]
	
args = []
for chunk in chunks:
	args.extend(baseArgs)
	args.extend(chunk)

system.db.runPrepUpdate(query, args)

```

---

<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:** [April 22, 2025, 10:06pm UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413/7 "2025-04-22T22:06:15Z")

</div>

And considering the return isn't being used, replace runPrepUpdate with runSFPrepUpdate to bypass waiting for it to be executed

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [April 23, 2025, 1:25am UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413/8 "2025-04-23T01:25:58Z")

</div>

> [@paul-griffith](#):
>
> _single_ `runPrepUpdate` for all 100 rows in the DB at once too.

I would only add to be cautious here as it may run up against whichever JDBC drivers max limit for parameters per query.

That generally around 2k but is still a limit so in some cases multiple updates will be required, depending on the situation.

---

<div class="post-metadata">

**Author:** ![michael.flagler](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/michael.flagler/32/26272_2.png) [@michael.flagler](https://forum.inductiveautomation.com/u/michael.flagler)\
**Post date:** [April 23, 2025, 4:36am UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413/9 "2025-04-23T04:36:22Z")

</div>

Am I missing something? Why are you running this in a client at all and not on the gateway? I don't see any need for this to run on the client.

---

<div class="post-metadata">

**Author:** ![bschroeder](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/bschroeder/32/13260_2.png) [@bschroeder](https://forum.inductiveautomation.com/u/bschroeder)\
**Post date:** [April 23, 2025, 12:17pm UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413/10 "2025-04-23T12:17:21Z")

</div>

> [@michael.flagler](#):
>
> Why are you running this in a client at all and not on the gateway?

This right here.  
Clients shouldn't be doing logging to a database. Gateways should.

---

<div class="post-metadata">

**Author:** ![Leonel](https://avatars.discourse-cdn.com/v4/letter/l/4491bb/32.png) [@Leonel](https://forum.inductiveautomation.com/u/Leonel)\
**Post date:** [April 29, 2025, 3:58pm UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413/11 "2025-04-29T15:58:23Z")

</div>

Okay, I have been following all of your tips and this is the result:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/d/3/d387b48caef7e7bcf97385c8ac954c0225b7a4aa.png)

Now the only problem is that I have some requests that are 100-200ms, which seems too high. I don't know if there's a way to find out which client/script is causing this, but when I close client #1, those long times disappear. I'll start checking all the scripts on client #1 to see if I see anything strange.  
Thanks everyone!

---

<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:** [April 29, 2025, 4:00pm UTC](https://forum.inductiveautomation.com/t/vision-client-request-sec-too-high/105413/12 "2025-04-29T16:00:33Z")

</div>

Those are reasonable numbers. By default, a Vision client will poll for tag updates every 250ms, so 44 of the 63 requests per second are accounted for.
