Web App concurrent user limitations

129 views
Skip to first unread message

Yagisanatode

unread,
Nov 11, 2020, 6:21:45 PM11/11/20
to Google Apps Script Community
Hi all, 

I just got some great feedback from Martin Hawskey on my introductory tutorial on creating a Web App regarding the limit on simultaneous executions being 30

I'm planning on providing a little more detail on this and some of the other limitations that Eric Koleda mentions in his post in Medium last year in my next post in the series. 

I have a few questions that I hope one of you might be able to answer: 
1. I had assumed that the withFailureHandler() would, if simultaneous exes >30 return the error - There are too many scripts running simultaneously for this Google user account. then from there, I could set a pause and then re-attempt the connection a few times before displaying an "I give up" message if the connection cannot be made. Does this sound correct to you? Will it do what I am assuming and display this message? Will, upon reaching the limit, just do a shutdown or all executions for the day or just for that brief time when executions are going bonkers?
2. I am planning on introducing LockService in the next of 3rd post on web apps to prevent submissions to a Google Sheet from being added prematurely. Have any of you experienced a withFailureHandler() error warning when using this?

Any advice would be appreciated. 

~Scott


Tanaike

unread,
Nov 11, 2020, 7:13:25 PM11/11/20
to Google Apps Script Community
Can I ask you about your script of "I had assumed that the withFailureHandler() would, if simultaneous exes >30 return the error"? Because I think that "withFailureHandler" is the method of Class google.script.run. Ref So I had thought that the limitation is not related to the maximum concurrent requests for the Web Apps. Ref For example, when the function of Google Apps Script for google.script.run runs the multiple requests for the Web Apps, I thought that the error by the Google Apps Script can be seen with "withFailureHandler". But, if I misunderstood your situation, I apologize.

2020年11月12日木曜日 8:21:45 UTC+9 Yagisanatode:

Adam Morris

unread,
Nov 11, 2020, 7:14:41 PM11/11/20
to google-apps-sc...@googlegroups.com
A bit of background onto the stack, which is why withFailureHandler may not be able to do what you think it's doing.

So when a user navigates to the web app, it executes the backend code for doGet. This will have a user associated with it, either the developer account or the user account (depending on your setting). What the doGet does is runs server-side code under that person. That's when you can hit the excess of 30 connections. 

The withFailureHandler is client-side code. The server-side code with doGet doesn't execute any client-side code, it just serves it. But if an error occurs before even doGet gets executed, there's no way your withFailureHandler code will have any bearing on the flow here.

The only time withFailureHanlder can catch server-side errors, is if the client navigates to a location.

Adam

--
You received this message because you are subscribed to the Google Groups "Google Apps Script Community" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-apps-script-c...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-apps-script-community/54247f52-4b9d-49cd-80b0-896eb88b51f7n%40googlegroups.com.

Yagisanatode

unread,
Nov 11, 2020, 7:36:02 PM11/11/20
to Google Apps Script Community
Thanks for the reply Tanaike,

Your stackoverflow link was really helpful. I'll adapt your code to run some tests. 

I noticed that withFailureHandler will essentially debug through the server-side function associated with google.script.run. My assumption was that if the script is set to run as owner and should there be 30 users all clicking run at the same time to initialise google.script.run that the error would cause the script to fail to work generating an error. I'll test this hypothesis with a modified version of your example. 

Very much appreciated. 

Yagisanatode

unread,
Nov 11, 2020, 7:48:06 PM11/11/20
to Google Apps Script Community
Hi Adam, 

Thanks for the in-depth reply. 

That does make sense when doGet serves the code for the first time under with connections >30. I definitely failed to consider the initial serving of the HTML. Should this occur, what happens client-side? A connection error or just a blank HTML page? Do you know?

If a server-side function is run concurrently with 30+ connections, will this results in a limit error too. For example, if 30+ users all managed to load the HTML page successfully, but they all clicked run at the same time executing a google.script.run. From what I read the withFailureHandler() does report connection issues along with server-side errors (like I discovered when testing). Would it report this excessive 30+ issue too?

Cheers, 

Scott

Tanaike

unread,
Nov 11, 2020, 9:21:14 PM11/11/20
to Google Apps Script Community
Thank you for replying. From your replying, I could understand about your situation as follows.

In your situation, 30 users open the Web Apps and then, all users run google.script.run, simultaneously.

I think that when the HTML is loaded and google.script.run is run at the single loaded page, it can be run with over 30 concurrent executions. This can be confirmed using the following script. In this script, no error occurs even when "google.script.run.sample(i)" is run 50 times with the asynchronous process.

HTML side:
<input type="button" value="download as PDF" onclick="for (let i = 0; i < 50; i++) google.script.run.sample(i);" />

Google Apps Script side:
const sample = e => console.log(e);

Unfortunately, I have no data for calling google.script.run with each browser of 30 users, simultaneously. But, if google.script.run is the same action like above, I think that over 30 concurrent executions can be used. Here, there is an important point. In your situation, if you are required to run the script for each user in order at Google Apps Script side, I would like to propose to use the LockService.

But, I'm not sure about your actual situation. So I'm not sure whether above information will be useful. I apologize for this.

Yagisanatode

unread,
Nov 11, 2020, 9:49:32 PM11/11/20
to Google Apps Script Community
Thanks again Tanaike,

I appreciate the detailed response and advice. 

I ran a quick test this morning after your reply and could not get an error yet from google.script.run on over 30 processes. I think as Adam mentioned, and what I wholly neglected, was the initial serving of the webpage with doGet(). That's more likely when the issue will occur if I have set Execute the app as me.  

I'll be covering lockService in relation to the next or third tutorial in this web app series to ensure that calls are doing what the user expects. I'm glad you recommended it too. It looks like I am headed in the right direction. 

Cheers,

Scott

P.S. Never any need to apologise to me, mate. You're a legend!

Tanaike

unread,
Nov 11, 2020, 10:21:14 PM11/11/20
to Google Apps Script Community
Thank you for replying. I understood your replying. I'm glad you could obtain the next direction from the discussions in this thread.

About "Execute the app as: Me", when this is changed to "Execute the app as: User accessing the web app", the user is required to log in to Google. And also, for example, when the values are put to your Google Spreadsheet in your script of Web Apps, it is required to share with users or publicly shared, and it is required to put the values using the spreadsheet ID. Please be careful them.

About more information of Web Apps, this information might be useful.

Pablo Felip

unread,
Nov 13, 2020, 12:14:13 PM11/13/20
to Google Apps Script Community
Hi, Tanaike / Adam / Scott,

El jueves, 12 de noviembre de 2020 a las 3:21:14 UTC+1, Tanaike escribió:
I think that when the HTML is loaded and google.script.run is run at the single loaded page, it can be run with over 30 concurrent executions. This can be confirmed using the following script. In this script, no error occurs even when "google.script.run.sample(i)" is run 50 times with the asynchronous process.

HTML side:
<input type="button" value="download as PDF" onclick="for (let i = 0; i < 50; i++) google.script.run.sample(i);" />

Google Apps Script side:
const sample = e => console.log(e);

I must be missing something, for sure, but I can't see why, in this case, more than 30 instances of a single apps script could run simultaneously without hitting the concurrency limit unless some of them had ended before others would have started, as spawned sequentially by the for loop in the js side. Have you tried this test with server-side code that needs more time to run?

In this situation, I don't see either how LockService could be of any help save for serialization of the script's instances, provided that run count be below 30, as the Lock calls themselves are executed in one of those instances, that add up to that 30 magic number. To my understanding, we are essentially in the same situation as Adam explained above regarding the doGet() part of the webapp.

Please, correct me if I am wrong.

Tanaike

unread,
Nov 13, 2020, 8:13:54 PM11/13/20
to Google Apps Script Community
Thank you for your comment. About your 2 questions, I would like to answer. Please think of this as one of several possible answers.

> I must be missing something, for sure, but I can't see why, in this case, more than 30 instances of a single apps script could run simultaneously without hitting the concurrency limit unless some of them had ended before others would have started, as spawned sequentially by the for loop in the js side. Have you tried this test with server-side code that needs more time to run?

I think that the maximum concurrent access of 30 is for Web Apps and the method of "Method: scripts.run" of Google Apps Script API. I think that "google.script.run" is not included in the maximum concurrent access of 30. For example, when the web apps are requested using a script with the asynchronous process, when the number of requests is over 30, the error occurs. https://stackoverflow.com/a/50033976/7108653

About "Have you tried this test with server-side code that needs more time to run?", for example, how about testing it using "Utilities.sleep(##)"?

> In this situation, I don't see either how LockService could be of any help save for serialization of the script's instances, provided that run count be below 30, as the Lock calls themselves are executed in one of those instances, that add up to that 30 magic number. To my understanding, we are essentially in the same situation as Adam explained above regarding the doGet() part of the webapp.

When LockService is used, the requests are processed one by one. I think that in this case, when you want to process the requests every 30 workers, it is required to think of the workaround for achieving it. I apologize for this.

Pablo Felip

unread,
Nov 15, 2020, 4:28:22 AM11/15/20
to Google Apps Script Community
Hi, Tanaike.

I think that the maximum concurrent access of 30 is for Web Apps and the method of "Method: scripts.run" of Google Apps Script API. I think that "google.script.run" is not included in the maximum concurrent access of 30

You are totally right, as usual :-), Tanaike. The number of max simultaneous executions for google.script.run "workers" does not seem to be 30, It was an unfortunate misconception on my side, I just took it for granted reading the official Apps Script docs about quotas and limits.

To verify it, I've put together this simple webapp. It does not really properly model how a purposeful webapp would spawn google.script.run requests in the real world, where users arrive / interact in a markovian manner. It just generates a parametrized (number of workers, time to run via Utilities.sleep) synthetic, sequential, workload on the server, but I feel that if the execution time for each of them gets higher enough than the time elapsed between each google.script.run call, results could give an idea of real-world performance.

I've attached a pic with some results depicting failed worker executions. It is clear that given the variability in success / fail, a try..catch approach with retries, maybe a binary exponential back-off mechanism, has to be put in place in the client part of the webapp.

So:
  1. When doGet method of any script is called (viewer access), each simultaneous instance counts to the official 30 magic number.
  2. Any google.script.run call spawns a server-side execution of a single script.
Even though there seem to be a much higher (although considerably variable ceiling) for how many server-side calls can be executed concurrently in (2)... well there is actually a limit. Is it documented anywhere in the official docs, or at least the rationale for it? (not to the best of my knowledge). Sometimes lower, well-known bounds are more reassuring than unknown/wildly variable ones. Maybe it is may OCD 😅, but I think I'd be happier with some specific upper bound for this.

Selección_794.png

Tanaike

unread,
Nov 15, 2020, 6:36:21 PM11/15/20
to Google Apps Script Community
Thank you for your additional information. From your image, in my environment, when "withSuccessHandler(function)" and "withFailureHandler(function)" are not used for "google.script.run", no error occurred with 50 workers. But at 100 workers, I confirmed some workers that the execution time is much longer than that of others. These workers finished the script of Google Apps Script. But, they are identified as "Unknown" at the Stackdriver. From this situation, I also think that "google.script.run" has the maximum number of concurrent requests. But unfortunately, I have never seen about this in the official document and other posts yet.

As the current workaround for avoiding this limitation of "google.script.run", I think that it is required to control the number of requests at Javascript side.

Reply all
Reply to author
Forward
0 new messages