Multiple Uplinks To Same App

How can I send a message out to all the uplink servers attached to my app?
Having a different hard coded function name for each one is a little awkward and makes adding new servers into the cluster a bit clunky. And I obviously can’t have multiple uplink servers with the same function names (tried it).

I suppose i could keep a list of unames in a db, then on the servers wrap the uplink code in a script that replaces the main function name with “my_func_” when run. That way I would call that function from the app (or cycle through all known ones from the db to broadcast), and the uplink code would automatically adapt for the server its on.

Any better suggestions? I’m tired and I might be overthinking this…

Load-balancing and high-availability between Uplinks is a feature we want to implement, but it’s not currently on the road-map for the next few months.

(You can change our minds! One benefit of an Anvil Support Plan is the ability to commission features like this, and move them up our road map. Contact support@anvil.works for more details.)

As for the specific workaround you suggest, having lots of unique function names will work. You might be interested to know that @anvil.server.callable can take an argument, and that lets you can register a function under whatever name you like. For example:

MY_ID = #...something..

if not app_tables.hosts.get(id=MY_ID):
  app_tables.hosts.add_row(id=MY_ID)

@anvil.server.callable("func_" + MY_ID)
def f():
  print "Hello from host %s" % MY_ID
# Broadcast
for host in app_tables.hosts.search():
  anvil.server.call("func_" + host['id'])
4 Likes

That works perfectly for my situation, thanks. I use the hostname as the function name, like this :

import anvil.server
import socket

host=socket.gethostname().replace("-","_")
...

@anvil.server.callable("fs_" + host)
...etc...

Will also timestamp the registration with the server (in the db) and have it re-register every minute or so to help identify dead processes.

Hi all,

Would you please provide an update to this thread? Is this the best method available to perform load balancing for clusters of anvil servers?

Sincerely,

What would constitute “best”, to you, in your current situation?

1 Like

What is the best way to scale an anvil server when I anticipate each user needing their own server instance or significant resources? For instance, putting my server on a kubernetes cluster, having the cluster add one pod when a user logs in and close when idle for a period of time. What is the best way to do this?

I don’t know anything about using Kubernetes clusters, and others will almost certainly have a better idea than this, but …

You can specify the uplink function names at runtime like this :

a_func_name = "something_unique_to_this_pod"
@anvil.server.callable(a_func_name)
def actual_func_name():
  ...

When the user logs in and you fire up a new pod using their API (can you do that?) and the uplink code runs, use something specific to that cluster as the function name (hostname, pod id, etc.). Then have it call a known function on the “main” server which registers that new function name (ie in a data table or in a session variable). Your current logged in user can then always call this uplink function on that specific pod.

Does that make sense or help in any way?

Yes, this does help. I just want to be sure that this is the “best” way to do this before I put in the effort. If anvil already has something built-in, I don’t want to be duplicative.

Thank you! @david.wylie

“Best” is a personal value judgement, so it’s always in the eye of the beholder. It’s easy for each person to assume that their idea of “best” – or parts of that idea – is everyone’s idea of “best”. In practice, that idea varies. A lot. From one situation to the next. From one person to the next. That (natural) difference of opinion has been known to lead to flame wars…

One antidote to this is to make your idea of “best” objective: concrete and explicit. Which specific aspects are most important to you at this point in time, and how they are to be measured.

Hi @Truth,

Anvil has moved on a little in the 3 years since this question was asked :slight_smile: If what you want is load-balancing, Anvil now does this for you!

When you call a function that’s available on multiple uplinks, Anvil chooses one of them and sends the call there. If you have lots of uplinks connected, Anvil will distribute the calls approximately evenly across all of them. So if you have a cluster of servers answering function calls, just run an Uplink process on each of them, and Anvil will share out the load.

(What @david.wylie was asking about at the beginning of this thread was something else - broadcast, rather than load-balancing - for which the approach he detailed upthread is still valid.)

3 Likes

I also read into it that @Truth was talking about firing instances of servers up on demand, rather than having them all running at the same time and therefore load balanced in the traditional way.

I may have misinterpreted that :slight_smile:

Note that an Uplink server may drop out or become unresponsive for a variety of reasons. When that happens to a server, how long does it take before Anvil notices, and starts sending calls to the other servers instead? Is there a monitoring API we can use? Or do we need to build one ourselves?

What I do is I attempt to restart the uplink every minute via cron (on Linux). I protect against multiple instances using this library :

Far from perfect and won’t suit everyone, but it suits me as most of my uplink scripts are sending back to the mothership rather than receiving requests.

Thanks, @david.wylie. For Windows users, AlwaysUp may do a similar job. I’m looking into it.

Either approach would take care of situations where the building still has both power and network connectivity. If either is lost, for whatever reason… well, we can run Uplinks from multiple locations. Then some kind of monitoring API becomes almost critical.

These questions are still very relevant.

Coming to this 5 years after the party!

I think this will be obvious to the original poster and responders but for others reading this thread in the future, here are two other system values you might consider using to “rename” your callable functions so they are individually addressable:

  1. MAC Address: Function name will be e.g. fs_0x163e990bdb
import uuid

@anvil.server.callable("fs_" + hex(uuid.getnode()))
def ...
  1. Local User Name: Function name will be e.g. fs_peter
import getpass

@anvil.server.callable("fs_" + getpass.getuser())
def ...

I believe these methods are platform-independent, but please correct me if anyone knows otherwise!

1 Like