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

shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
Werner
Hi, I’m using Ubuntu 18.04 and after updating to OTP-24.0 yesterday i have this warning when I run “mix local.hex”: 14:57:30.512 [warn] ...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
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
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
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
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New

We're in Beta

About us Mission Statement