amnu3387
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
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #blog-post
- #elixirconf-us
- #elixir-ls
- #ai
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming











Showing Posts 1 to 6- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
idi527
What’s in
create(game_id)?btw, there is no need to wrap atoms in tuples. It seems to me that
is a bit cleaner than
amnu3387
I agree, leftovers not yet cleaned up.
It’s this
In case this helps:
And the start_link from the genserver:
(and for the lolz your message pops up as “idiot blablabl” and I was like, wow this guy starts a bit harsh)
amnu3387
I think I’m doing something wrong on the initialisation because I get two processes when I inspect with the observer.
But what is weird is that, when I join the channel, it fetches one process
When I inspect that on observer it has the initialised state of a game that has just started.
But when I call an action or something it gives me the actual updated process:
So I’m probably doing something wrong with how I’m initialising the Supervisor and the child processes?
But they’re being called by the same ref id?
idi527
Sorry, got busy for a moment.
Can you put some
IO.inspectorLogger.debugaround the values returned in channel’sjoin(especiallystats) and compare them with what you get in the client?No, I don’t think they do.
Can you show the code for
AetherWars.Duels.Monitor.ref(game_id)?With
:simple_one_for_onestrategy it might be a good idea to useRegistryand keep track of the child processes there. Note that atoms fromref(game_id)won’t be garbage collected.Then your
start_linkwould look like thisCan’t see the reason for duplicated processes though …
amnu3387
So basically I patched a bunch of articles and the documentation to get to this point, so it can be obviously wrong:
What I had understood was that :game was gonna be a atom but not game_id (if it’s so then I should really change that).
What’s been tripping me up is that it basically does this (I’m passing it all from the init console output), you can see that first it initiates a Genserver for game 5, but then when the second user joins the game, it creates another one because it can’t seem to find an existing reference - perhaps this is an issue of being too concurrent?? shouldn’t be right?
But as soon as I do anything that requires interacting with the Genserver (pass, cast, etc), it correctly references the last pid. By what I read I was thinking that whenever you have a global ref if you try to duplicate one of them is terminated by default?
What’s weirder is that on joining, given a ref for game 5, it fetches the first initiated process, and then when calling other stuff it fetches the second process. Multiple refreshes don’t create additional processes, so I think I’m missing something on the instantiation of the child processes?
Thanks for looking into it - it’s not crucial right now - but I would like to understand what I’m doing wrong.
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