Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duplicate subscriptions sent when same user is logged in with different browsers #83

Open
dlobo opened this issue Feb 22, 2021 · 11 comments

Comments

@dlobo
Copy link

dlobo commented Feb 22, 2021

We are using subscriptions a fair bit to communicate the state of messages between client and server. At times, the same user will log in via two different browsers.

When this happens, we have the same subscription sent twice to BOTH browsers. They also have the same subscription id.

We have the user object in the absinthe context. We also are using publish_data to publish the subscriptions from the code base (and not via the schema)

@coladarci
Copy link

Isn't this "on you" ? I.e you have to "scope" the join w/ a session ID you create; that's what we are doing, at least...

@dlobo
Copy link
Author

dlobo commented Feb 22, 2021

Isn't this "on you" ? I.e you have to "scope" the join w/ a session ID you create; that's what we are doing, at least...

can you elaborate on what you mean by "scope" the join. Example code would be great. we can definitely do it from different browsers (since its different auth tokens). not sure if we can do it with different tabs from the same browser

@coladarci
Copy link

Yeah, in your connect function you can authenticate and also store anything else you want to identify the socket. We generate a session id client side and store this.

def connect(%{"token" => token, "session_id" => session_id}, socket) do
    case API.Identity.authenticate(token) do
      {:ok, user} ->
        socket =
          socket
          |> assign(:user, user)
          |> assign(:session_id, session_id)
          |> Absinthe.Phoenix.Socket.put_options(
            context: %{
              current_user: user
            }
          )

        {:ok, socket}

      :error ->
        {:error, %{reason: "unauthorized"}}
    end
  end

@dlobo
Copy link
Author

dlobo commented Feb 22, 2021

yeah, we are storing a session_fingerprint which we get from the authentication library (pow) for each socket connection, so similar to your code above.

However if i had to guess the session_fingerprint is the same for multiple tabs within the same browser (since they are using the same token)

@coladarci
Copy link

Yes, typically the session is stored in local storage which is shared across tabs..

@dlobo
Copy link
Author

dlobo commented Feb 22, 2021

are you doing anything special with publish when you are publishing data onto the subscription? all our subscriptions are based on the organization, so all users from the same organization, get the same data subscriptions

I checked and the fingerprint is different, so we are scoping the "join" (I think)

@coladarci
Copy link

Sorry - we are also always publishing to an entire organization for ABSINTHE. We are doing target socket pushes ourselves using presence... So many there's a real bug?

@benwilson512
Copy link
Contributor

@coladarci you said that each client is getting an identical subscription ID when subscribing? If so, that bit sounds like the bug to me.

@coladarci
Copy link

No I was making a bad assumption - I got my signals crossed here confusing how we handle absinthe publishing versus our other publishing w/ sockets directly.

@benwilson512
Copy link
Contributor

OK so each client is getting a different subscription id, but if you publish, both get it? Can you make this more concrete?

@mupkoo
Copy link

mupkoo commented Apr 21, 2021

I might have the same problem. Here is what I have:

# Web.Schema.Question
object :question_subscriptions do
  field :question_change, :question_change do
    arg :parent_id, non_null(:id)
    config fn %{parent_id: parent_id}, _ -> {:ok, topic: parent_id, context_id: "global"} end
  end
end

# Web.Schema
subscription do
  import_fields :question_subscriptions
end

# After some save operation
Absinthe.Subscription.publish(
  Web.Endpoint,
  %{action: action, question: question},
  question_change: question.parent_id
)

Now depending on how many clients I have connected, I get the same number of pushes and all clients receive the data.

Here are the logs when I have a Safari, Firefox and Chrome windows connected and all three browser windows receive the payload thrice. If I close one of the windows, the number drops down to 2.

---
[debug] QUERY OK source="questions" db=5.2ms idle=35.0ms
SELECT q0."id", q0."name", q0."body", q0."is_visible", q0."parent_id", q0."speaker_id", q0."inserted_at", q0."updated_at" FROM "questions" AS q0 WHERE (q0."id" = $1) [44]
[debug] QUERY OK db=3.7ms queue=3.1ms idle=42.1ms
UPDATE "questions" SET "name" = $1, "updated_at" = $2 WHERE "id" = $3 ["Name", ~U[2021-04-21 09:24:37Z], 44]
[debug] Absinthe Subscription Publication
Field Topic: 1
Subscription id: "__absinthe__:doc:global:2FD0104E90140DE66FC1D279921023676216746C4760C5C3CF35637D6813451B"
Data: %{data: %{"questionChange" => %{"__typename" => "QuestionChange", "action" => "UPDATED", "record" => %{"__typename" => "Question", "body" => "Body", "id" => "44", "insertedAt" => "2021-04-21T08:12:36Z", "isVisible" => true, "name" => "Name"}}}}

[debug] Absinthe Subscription Publication
Field Topic: 1
Subscription id: "__absinthe__:doc:global:2FD0104E90140DE66FC1D279921023676216746C4760C5C3CF35637D6813451B"
Data: %{data: %{"questionChange" => %{"__typename" => "QuestionChange", "action" => "UPDATED", "record" => %{"__typename" => "Question", "body" => "Body", "id" => "44", "insertedAt" => "2021-04-21T08:12:36Z", "isVisible" => true, "name" => "Name"}}}}

[debug] Absinthe Subscription Publication
Field Topic: 1
Subscription id: "__absinthe__:doc:global:2FD0104E90140DE66FC1D279921023676216746C4760C5C3CF35637D6813451B"
Data: %{data: %{"questionChange" => %{"__typename" => "QuestionChange", "action" => "UPDATED", "record" => %{"__typename" => "Question", "body" => "Body", "id" => "44", "insertedAt" => "2021-04-21T08:12:36Z", "isVisible" => true, "name" => "Name"}}}}

[debug] -- Absinthe Phoenix Reply --
{:ok, %{data: %{"updateQuestion" => %{"__typename" => "Question", "body" => "Body", "id" => "44", "insertedAt" => "2021-04-21T08:12:36Z", "isVisible" => true, "name" => "Name", "speaker" => nil}}}}
----------------------------

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants