Multiple, simultaneous user sessions?

Suppose I would like two users, A and B, to simultaneously use my online Anvil app for image classification.

A first uploads an image using the file uploader.

img_to_classify = img_that_A_uploaded

B then uploads an image also using the file uploader. As a result, the img_to_classify variable gets overwritten by B’s image

img_to_classify = img_that_B_uploaded #img_that_A_uploaded was overwritten

When A presses the Classify() button, the model would process B’s image instead.

I wonder if anyone have ideas on how to build an Anvil app such that it can support multiple, simultaneous user input without overwriting behavior? For example, multiple instances with Uplink?

I’m currently hosting my Anvil app locally using a Jupyter notebook with Uplink.

This is, in fact, the default. Here is how it works.

  1. When User A visits your App’s web page, their browser receives its own copy of the App’s Client modules, and any modules that they import. This is a Python program running in A’s browser, on A’s own machine.
  2. The same thing happens with User B, C, D, … Each gets their own independent copy, or instance, running independently.
  3. Because these copies are all independent, they do not share any variables. However, where some kind of sharing is necessary, they can call upon Anvil’s database features, to save values in a database shared by all copies of the App.
  4. When any of these instances calls a Server Module function, Anvil starts a new copy of a Server-side program, containing that function, to service the call. That program generally runs until the function completes.
  5. If the called function is in an Uplink program, it is your responsibility to make sure that the Uplink program has already been started, so that it can respond. Each incoming request starts in its own Python thread of execution, so calls may overlap. (Be careful with any global variables in the Uplink program!)

Edit 1: I strongly encourage you to use the Docs link above, and the Search feature, for more detail on these topics. Search reaches into the docs and this forum, so it can find answers for you in both places.

Edit 2: If Uplink is essential to the App’s operation, then your Uplinked functions must be thread-safe, in the Python sense. Two or more instances of the Uplinked function (or functions it calls) may be running at the same time, so they should not be writing to the same global variable. (This extends to other shared global resources, such as local database connections, or files.) You don’t want a call from User A to screw up a call from User B. Function-local variables are fine, though, as each instance gets its own local variables.

2 Likes

@p.colbert I am using global variables in my Python program (which calls Uplink)! I will try to not use global variables and update you my results.

Also, should I also not use global variables within the client code of Anvil?

That is usually not a problem. Each user runs their own independent copy of your Client code. Each copy has its own global variables, not shared with other users. In fact, they’re not even shared with other browser windows/tabs!

1 Like

Is there documentation detailing all the content types supported by BlobMedia (e.g., text/plain)?

Since I’m avoiding global variables, I’m returning the variables back to Anvil (which requires them to be Media Objects). However, I’m struggling with converting various object types (e.g., PyTorch tensors) into Media Objects.

I have always understood BlobMedia as a “catch-all”, for binary data of any sort.

If you know the content_type code (i.e., if it’s a standard MIME type), by all means provide it. This way, if the binary data happens to be, for example, an image to be displayed in an Anvil component, then Anvil (or the browser) will be able to interpret it correctly.

If your data’s type does not appear in the standard, then to make that explicit, you might use a custom (private) type, as allowed by the MIME standard.

1 Like

May I ask if this does this not apply to Server code? For example, global variables in Server code are shared across all users.

That happens in Uplink code, but not Server code.

So, the Server-side program (with its global variables) does not “live long enough” to share anything. This is mainly to prevent security problems. If the Server-side program kept running, and was reachable from both User B and User A, then you might have to work fairly hard to prevent User B from mangling user A’s data (or vice versa). But the way Anvil works, you scarcely have to think about it.

There is a way to preserve global data. See Sessions and Cookies. These methods are strictly limited, to keep users from stepping on each others’ data.

1 Like

Thanks for the explanation! If the Server Code allows global variables that are not shared across different users, I think it would make sense to put my Python program in the Server Code instead of using Uplink (so that there wouldn’t be Media Object file conversion hurdles).

The only issue (related to my other question post) is that I cannot use custom libraries in the Anvil server. On a paid plan, I still wouldn’t have access to very specific libraries that I can simply pip install on Colab. Many of these libraries are Github repos.

I wonder if there’s a way to install your own libraries for your server-side program, such as through a requirements.txt file, instead of having to request them to be manually installed by Anvil.

That’s possible, if you’re willing to install an entire Anvil Server on one of your own computers. The open-source version can run one App at a time.

Edit: Or you could split the functionality. That is, do as much as possible at Anvil’s server, and do only the remainder via your Uplink code.

Not sure whether this would be helpful, but I thought of this thread when I stumbled across this:

Registering your server function under a different name

Server functions don’t have to take the name of the function they’re decorating. You can pass a string into the decorator to anvil.server.call them by a different name.

@anvil.server.callable("my_func")
def foo():
  return 42

This can be calculated at runtime, so if you have multiple Uplink connections, you can append an ID based on which instance of the Uplink you’re connecting from.

import uuid
UPLINK_ID = uuid.uuid4()

@anvil.server.callable("my_func%s" % UPLINK_ID)
def foo():
  return 42

This is the section of the docs that this comes from.

2 Likes

A post was split to a new topic: Two users’ function calls interfering with each other?