beltrewilton

beltrewilton

Hey fellow devs,

This is my first post in this awesome community, and I’m excited to share my project with you all. As a newbie, I’d love to get your feedback on my design.

Here’s the context: I have an umbrella project with two apps. The first app is a gateway that receives a high volume of messages from Meta’s WhatsApp Cloud API. These messages are destined for different business account numbers (in Meta’s jargon). To simplify, imagine receiving messages like {"1800555", "message 1 from whatsapp consumer"}, {"18007777", "message 2 from another whatsapp consumer"}, and so on.

The second app is a bot that processes these messages. Now, here’s how my architecture works: the gateway (a Plug endpoint) is a node called gateway_node@127.0.0.1. When a message arrives, it reads from Mnesia, which has a map of business accounts to destination nodes. The destination node is a copy of the bot app (app 2), which can be something like customer_services_node@127.0.0.1 or another_company@127.0.0.1.

The advantage of this setup is that I can add nodes (business accounts) on demand without affecting the others.

So, what do you think? Is this approach too newbie-ish? Any recommendations?

Thanks in advance!

Showing Posts 1 to 6

al2o3cr

al2o3cr

Node-per-tenant is somewhat unusual; more commonly you’d a setup where a DynamicSupervisor on a single node manages one tree of processes per bot, roughly like:

This lets all the instances of Bot share the rest of the BEAM support machinery, versus having separate copies in each client’s node.

This may not be possible if instances of Bot need a lot of resources - for instance, if you’re running LLM inference on a GPU in them - in which case splitting onto different physical nodes may be the only option.

BartOtten

BartOtten

But you need to restart the app to add a business. That is what he tries to avoid.

krasenyp

krasenyp

Why won’t OP be able to do it without a restart? They can use the remote shell to trigger a function for creating a business. There’s a myriad other ways.

BartOtten

BartOtten

Sorry, I was already half sleeping and missed the ‘Dynamic’ part of ‘DynamicSupervisor’ :pensive_face:

beltrewilton

beltrewilton OP

Thanks @al2o3cr so much for the insight. I think I need to dust off my notes from Sasa Juric’s book to fully wrap my head around the DynamicSupervisor setup you described. Appreciate the guidance, and I’ll definitely consider the resource implications for the bot instances.

Totally get it @BartOtten, no one wants to restart the app mid-convo :slight_smile:

@krasenyp I’ve explored a solution that involves storing a table in memory (using :mnesia), connecting to this remote-node, and establishing a business account relationship with the new node.

The downside is that the entire app doesn’t interrupt conversations, plus the ability to create new business accounts ‘on the fly’. Each bot app has its own state, conversation history, and client profile, and it’s crucial that this info is isolated between business accounts.

On top of that, I’m using rpc.call rpc.cast to route to the destination node (The bot). The bot calls a LLM, and depending on the state, it performs long-running processes, image and audio processing, external API calls, and even calls a Pytorch model.

I’m sharing a snippet of the code that decides which node to send the request to. Guys, it’s working for now, but I’m not sure if it’s good practice. Any recommendations are welcome!

bussines_account_id = "...." # get from Meta request

case MyApp.discover_node_target(bussines_account_id) do # read from :mnesia
      [_, target_node, :target_app_name] ->
        :rpc.cast(
          target_node,
          MyApp.StateMachine,
          :new,
          [
            bussines_account_id,
            Keyword.get(sender, :sender_phone_number),
            Keyword.get(sender, :message),
            Keyword.get(sender, :wa_message_id),
            Keyword.get(sender, :flow),
            Keyword.get(sender, :audio_id),
            Keyword.get(sender, :scheduled),
            Keyword.get(sender, :forwarded)
          ]
        )
...

# output example
iex(node1@10.0.0.28)1> MyApp.discover_node_target("742393808949918") 
["742393808949918", :"customer_service@10.0.0.28", :target_app_name]

AnaisW

AnaisW

I’m curious whether you’ve tested how Mnesia behaves under heavy concurrent bot traffic across nodes. I’ve seen some folks switch to ETS plus a lightweight sync process because Mnesia got chatty in clustered setups. Did you run into anything like that, or does your layout keep the load balanced well enough?

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews