benonymus
How to keep track of potentially 1000s of events participant cap's change?
Hey there,
I have the following problem:
I have events in my database, a lot of them, and the list will only grow.
Every event can have multiple ticket types, and each ticket type has a quantity field.
The sum of the quantity fields for given event’s ticket types is the participant cap.
I want to introduce a waiting list option so if a spot frees up for any reason, being someone leaving or the total participant cap growing I want to send an email to the users on the waiting list.
How do I keep track of the change of the participant cap?
I could create just a function that accepts 2 instances of an event and compare the participant cap, so I can pass it one event being before any action and one after and if the part cap is changed then send the emails.
cons for this is that it is very manual, need to find the places where it is relevant in the code, pro is that it is pretty simple.
Other option could be to have a genserver for each event, keeping the current participant cap for each event, and tracking if messages have already been sent or not. Maybe using:
https://elixirschool.com/blog/til-genserver-handle-continue/
But isn’t this approach very wasteful?
Also somehow would need to make sure that every events has a genserver running, even new ones that get inserted.
Any thoughts on ether of the approaches? Maybe other ideas?
Thanks!
Marked As Solved
benonymus
Okay, I ended up implementing the Postgres notifications approach mostly based on this article:
It seems surprisingly good so far!
Thanks everyone for the messages!
Also Liked
chouzar
It sound like you could take advantage of both approaches.
If all you need to achieve the task is procedural code maybe you don’t need a GenServer:
def compare(event_1, event_2), do: send_email("email")
But you would need to have received both events to execute this function. If events come and go at different times you could use laziness to defer computation:
def lazy_compare(event), do:
(fn later_event -> compare(event, later_event) end)
And just wait until your next input is ready so you can execute the returned function at a later time.
If you really don’t need the GenServer, yes it is a wasteful (but cheap) approach. A GenServer would be a solution to handle asynchronous calls; just be careful because you may already have this in the form of a Phoenix Server, HTTP Request or iex session, but if you’re creating your custom “subscription / publish” mechanism a GenServer would be a great way to go.
If you haven’t, I suggest you read the to spawn or not to spawn article by @sasajuric; it may help to re-arrange some thoughts on when and where to use processes.
riebeekn
btw, if you end up going the Postgres notifications route, I found this article (and the other article the author links to) useful in getting an initial handle on how notifications work: https://medium.com/@kaisersly/postgrex-notifications-759574f5796e
chouzar
Sounds a lot like CQRS, and to be honest that kind of pattern really falls outside my area of expertise
.
But if you would like a separate entity to take care of the handling would Task.Supervisor or DynamicSupervisor work for you? That way you would could spawn a process “on request”.
chouzar
Definetly.
Ok, so I think I’m starting to have a clearer picture of the approach you want. When an event “goes live” you would like to track its capacity in real-time.
Thinking in “logical terms” here I think if its just 1 or multiple GenServers makes no difference. The purpose of this entity is to track 1 or multiple event caps.
Having multiple GenServers would give you reliability per event in case of failure (assuming you’re not storing on disk or a database, a process crash would lose the tracked event cap). And it may be also an optimization (or not) because you’re taking better advantage of available resources.
chouzar
I just noticed a similar threath at Should I use GenServer for this? - #5 by lucaong ![]()
Yeah, so with a GenServer you could “poll the database” each x seconds to keep track of current state.
You could keep a “cache” whenever the cap changes (so you would change it in the database, but would also keep it in memory for real-time purposes). But this sounds clumsy if you have the database at hand.







