Which architecture to use for complex PubSub + nested liveviews?

I’m exploring a complicated LiveView + Genserver design, and I could use some pointers towards ideas I don’t know about yet. I suspect I’m missing some steps or making some mistakes with the design of this.

It’s an app that tracks vehicle locations for clients. Assume we’re tracking 50-1000 vehicles at a time.
Vehicles send a location update every ~minute.

  • A Vehicle module with a function that receives a vehicle ping, logs it to the database, then Publishes a VehicleUpdate pubsub message with the update.
  • A genserver VehicleTracker process, one running for each vehicle in the field. This process has the current state + location of the single vehicle (in-memory cache of the database information) received via subscription from the Vehicle module. This process then runs a series of regular checks and might update its current state. E.g. if no location pings received in the last 5 minutes, mark self as “Out of contact”. This is computed state, so this an in-memory process feels like the right place for it.
  • A genserver StatsTracker process that does some aggregate statistics across a subset of vehicles (e.g. all the vehicles for CompanyA). These statistics might be things like “Number of vehicles inside the city limits of City1”. This builds a map of statistics, and when something changes it will broadcast a StatsUpdate message with the new stats map.
  • A TrackerLive Liveview, which subscribes to both the VehicleUpdate and StatsUpdate messages. On mount, we fetch the intitial state from our VehicleTracker and StatsTracker processes, and then after that we use the data which is pushed with every PubSub message.

My specific questions are:

  1. Does this structure seem ok? Anything obviously wrong?
  2. I chose the VehicleTracker and StatsTracker because LiveViews are ephemeral, and many users might want to view these same sets of data, so calculating them in each liveview is a waste. (Also, there is long-running state like “How many vehicles have checked in in the last 5 minutes” that a long-running process can better handle). Is this best practice, to have a seperate long-lived process that liveviews consume from?
  3. With the liveview subscribing to data updates, which of these options is best? (I suspect option 1 but welcome input)
    A. Receive the message, consume the contained Vehicle data from the message.
    B. Receive the message, ignore the contained data, fetch the data from the relevant Process directly (with Genserver.call).

I’m interested in specific details: how this could break LiveView diffing, how this might trigger race conditions, etc.

Thank you :heart: