amnu3387

amnu3387

Issue with Genserver state on first call

So I’m not sure this is an issue with genserver itself, the channel communication, or the way I’ve got the JS hooked up. The architecture is as:

  • User loads the page, JS imports happen, a VueJS component is instantiated, on the mount trigger for the component the user connects to a general channel for this “duel”. The response from joining includes the state of the game, which is used to populate the VueJS component and a another call to join a user particular channel is made.

The problem I’m running into is that, if I have the game going and state has been altered, and then I refresh the page, the state I’m being served is stale on the first connection. If I then take an action the update broadcast actually shows the correct state (and updates the vuejs component accordingly). My question is why is the gen state serving a stale state on the beginning and then serving the correct one afterwards?

The code is as follows:

#js side

imports {.... }

export default class View extends Shared {
  ...
  Vue.component('Game', Game)
    new Vue({
      el: 'main',
      data() {
        return {
          ...
        }
      },
      mounted() {
        this.channel = socket.channel("duel:"+window.gameId, {token: window.userToken});
        this.channel.join()
          .receive("ok", response => {
            this.user_id = response.user_id;
            ...
           joinUserChannel(this, response.user_id);
    }
  }
}

#channel side

def join("duel:" <> id, payload, socket) do
    case authorized?(payload, socket, id) do
      {:ok, socket} ->
        stats = Monitor.game_stats(id) |> Processor.clean_stats(socket.assigns.current_user)
        {:ok, %{user_id: socket.assigns.current_user, game: stats}, socket}
      {:error, _} ->
        {:error}
    end
  end

 def handle_in("pass", %{"game_id" => game_id}, socket) do
    IO.puts("handle in pass")
    stats = Monitor.game_stats(game_id) |> Processor.pass(socket.assigns.current_user)
    |> broadcast_this
    {:noreply, socket}
 end

#monitor

defmodule AetherWars.Duels.Monitor do
  @moduledoc """
  Keeps and serves Game State
  """
  use GenServer
  alias AetherWars.Duels.Processor
  alias AetherWars.Games
  ...
  def start_link(game_id) do
    IO.puts("Monitor start_link -> game #{game_id}")
    GenServer.start_link(__MODULE__, game_id, name: ref(game_id))
  end

  def init(game_id) do
    IO.puts("Initializing Game #{game_id}")
    game = Games |> Games.players |> AetherWars.Repo.get(game_id)
    case game.game do
      nil ->
        IO.puts("Initializing Game #{game_id} - doesn't exist")
        state = Processor.create(game)
        changeset = Ecto.Changeset.change game, game: state
        AetherWars.Repo.update changeset
        {:ok, state}
      game ->
        IO.puts("Initializing Game #{game_id} - exists")
        state = game
        {:ok, state}
    end
  end

def game_stats(game_id) do
    IO.puts("game_stats game: #{game_id}")
    try_call game_id, {:game_stats}
end

...

def handle_call({:game_stats}, _from, state) do
    IO.puts("handle_call game_stats")
    {:reply, state, state}
end

defp try_call(game_id, call_function) do
    case GenServer.whereis(ref(game_id)) do
      nil ->
        IO.puts("try_call whereis nil")
        case create(game_id) do
          {:ok, pid} ->
            GenServer.call(pid, {:game_stats})
          _ ->
            {:error}
        end
      pid ->
        IO.puts("try_call whereis not nil match")
        IO.inspect(pid)
        GenServer.call(pid, call_function)
    end
 end

So what I’m not understanding is why the first call to Monitor.game_stats(game_id) on the join seems to return a stale state but as soon as I make any other action that triggers the same Monitor.game_stats the returned state is actually the updated version?

Am I missing something obvious? I could make it ask explicitly for the game state after joining once again, but shouldn’t it be serving the correct state right away? The only difference when making the call is that it then broadcasts them to the particular channel of the user, but the data is loaded into the vuecomponent the same way (and indeed on the join it’s loading data, but just as if the state was the init state).

(do channels cache their response? because then a refresh of the page could mean the cached response was being served over the channel? it doesn’t seem to make sense though…)

Thanks

Most Liked

amnu3387

amnu3387

Oh - you know what was the problem? When the id was being taken from the channel topic with <> it’s a string, but when being sent through the channel as a message parameter, I was sending it as an integer.
I noticed it by using IO.inspect instead of IO.puts on the id’s - and there they were, “5” and 5… so there goes the mystery. Thanks for looking at it, the fact you mentioned they would be :atoms made me look again and find this :wink:

Where Next?

Popular in Questions Top

srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
gonzofish
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
hpopp
To simplify some tasks at work, I wrote and published this package yesterday. It’s a simple macro that enables Access behaviour on struct...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New

We're in Beta

About us Mission Statement