Open
Description
Bug report
Describe the bug
For some unknown reason that I'm still trying to debug, my client is losing the connection to the socket. But besides that, once it reconnects, the SocketStateEventHandler is not being called.
Here is the Manager i'm using:
See code
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json;
using UnityEngine;
using Supabase;
using Supabase.Models;
using Supabase.Realtime;
using Supabase.Realtime.Interfaces;
using Supabase.Realtime.Models;
using Supabase.Realtime.PostgresChanges;
using static Supabase.Realtime.PostgresChanges.PostgresChangesOptions;
using static Supabase.Realtime.Constants.ChannelEventName;
using TcgEngine;
using UnityEngine.Events;
using Client = Supabase.Client;
public class SupabaseManager : MonoBehaviour
{
public UnityAction<string, string> onMatchRequest;
private const string SupabaseURL = "https://redacted.supabase.co";
private const string SupabasePublicKey = "redacted";
private Client _supabase;
private RealtimeBroadcast<UserBroadcast> _userBroadcast;
private static SupabaseManager instance;
private readonly Dictionary<string, RealtimeChannel> channels = new Dictionary<string, RealtimeChannel>();
private void Awake()
{
instance = this;
}
async void Start()
{
Debug.Log("SupabaseManager start");
string url = SupabaseURL;
string key = SupabasePublicKey;
if (_supabase == null)
{
var options = new SupabaseOptions
{
Headers = new Dictionary<string, string>
{
["Authorization"] = "Bearer " + ApiClient.Get().AccessToken
}
};
Debug.Log(ApiClient.Get().AccessToken);
_supabase = new Client(url, key, options);
await _supabase.InitializeAsync();
}
_supabase.Realtime.AddDebugHandler((sender, message, exception) => Debug.Log(message));
_supabase.Realtime.AddStateChangedHandler(SocketEventHandler);
_supabase.Realtime.SetAuth(ApiClient.Get().AccessToken);
var realtimeClient = await _supabase.Realtime.ConnectAsync();
Debug.Log("Is connected? " + realtimeClient.Socket?.IsConnected);
}
private void OnDestroy()
{
Debug.Log("SupabaseManager OnDestroy");
if (_supabase != null)
{
foreach (var channel in channels.Values)
{
_supabase.Realtime.Remove(channel);
}
channels.Clear();
_supabase.Realtime.RemoveStateChangedHandler(SocketEventHandler);
_supabase.Realtime.Disconnect();
_supabase = null;
}
}
private void PostgresUpdatedHandler(IRealtimeChannel _, PostgresChangesResponse change)
{
Debug.Log(change);
if (change.Payload?.Data?.Table == "users")
{
//User data changed
var model = change.Model<User>();
var user = Authenticator.Get().UserData;
if (model != null)
{
if (model.friends_requests.Length > user.friends_requests.Length)
{
//New friend request. Notify in UI
Debug.Log("New friend request");
user.friends_requests = model.friends_requests;
}
}
}
}
private void BroadcastEventHandler(IRealtimeBroadcast broadcast, BaseBroadcast state)
{
if (state != null && state.Event != null)
{
switch (state.Event)
{
case "matchRequest":
if (state is UserBroadcast userBroadcast && userBroadcast.Payload != null)
{
//Debug.Log($"Show match invitation: {JsonConvert.SerializeObject(userBroadcast, Formatting.Indented)}");
onMatchRequest?.Invoke(userBroadcast.Payload.MatchRequestFrom, userBroadcast.Payload.Key);
return;
}
break;
}
}
Debug.Log($"Unknown broadcast: {JsonConvert.SerializeObject(state, Formatting.Indented)}");
}
private async void SocketEventHandler(IRealtimeClient<RealtimeSocket, RealtimeChannel> sender, Constants.SocketState state)
{
var username = ApiClient.Get().Username;
Debug.Log($"Socket is ${state.ToString()} and username is ${username} and user_id is ${ApiClient.Get().UserID}");
channels.TryGetValue("users:" + username, out RealtimeChannel personalUserChannel);
if (personalUserChannel == null)
{
personalUserChannel = sender.Channel("users:" + username);
channels.Add("users:" + username, personalUserChannel);
_userBroadcast = personalUserChannel.Register<UserBroadcast>(false, true);
_userBroadcast.AddBroadcastEventHandler(BroadcastEventHandler);
}
channels.TryGetValue("public:users", out RealtimeChannel userTableChannel);
if (userTableChannel == null)
{
userTableChannel = sender.Channel("public:users");
channels.Add("public:users", userTableChannel);
userTableChannel.Register(new PostgresChangesOptions("public", "users", ListenType.Updates, "id=eq." + ApiClient.Get().UserID ));
userTableChannel.AddPostgresChangeHandler(ListenType.Updates, PostgresUpdatedHandler);
}
if (state == Constants.SocketState.Open)
{
foreach (var channel in channels.Values)
{
await channel.Subscribe();
}
}
else
{
foreach (var channel in channels.Values)
{
channel.Unsubscribe();
}
}
}
public async Task<bool> SendMatchRequestBroadcast(string username, string key)
{
channels.TryGetValue("users:" + username, out RealtimeChannel channel);
if (channel == null)
{
channel = _supabase.Realtime.Channel("users:" + username);
channels.Add("users:" + username, channel);
var broadcast = channel.Register<UserBroadcast>(false, true);
broadcast.AddBroadcastEventHandler(BroadcastEventHandler);
}
var data = new UserBroadcast { Event = "matchRequest", Payload = new UserStatus { MatchRequestFrom = ApiClient.Get().Username, Key = key } };
if (!channel.IsSubscribed)
{
await channel.Subscribe();
}
if (channel.IsJoined)
{
return await channel.Send(Broadcast, null, data);
}
else
{
Debug.LogError("Channel not joined. Try to rejoin?");
}
return false;
}
public static SupabaseManager Get()
{
return instance;
}
}
And you can see in my logs where the socket gets disconnected (I left the stacktraces for those in case it gives some hints) but after each of the Socket State Change
I was expecting to see the Socket is $state and username is $username and user_id is $user_id
line and that never happened
Logs
SupabaseManager start
Socket Reconnection: Initial
Socket Connected to: wss://redacted4&vsn=1.0.0
Socket State Change: Open
Socket is $Open and username is $Lothar and user_id is $1
Socket Push [topic: phoenix, event: heartbeat, ref: ec68999f-f9d4-41bc-aa55-849efc1f97c0]:
{}
Socket Push [topic: realtime:users:Lothar, event: phx_join, ref: 0245d09f-3b51-4d9a-93df-0a6d87b9f3e8]:
{
"config": {
"broadcast": {
"self": false,
"ack": true
},
"presence": {
"key": ""
},
"postgres_changes": []
}
}
Is connected? True
Socket Message Received:
{"event":"phx_reply","payload":{"response":{},"status":"ok"},"ref":"ec68999f-f9d4-41bc-aa55-849efc1f97c0","topic":"phoenix"}
Socket Message Received:
{"event":"phx_reply","payload":{"response":{"postgres_changes":[]},"status":"ok"},"ref":"0245d09f-3b51-4d9a-93df-0a6d87b9f3e8","topic":"realtime:users:Lothar"}
Socket Push [topic: realtime:users:Lothar, event: access_token, ref: 71b840bc-4f6d-4d89-aba2-f45d0b7c6926]:
{
"access_token": ""
}
Socket Push [topic: realtime:public:users, event: phx_join, ref: df8a4e15-4830-4e91-9675-5b58a70482e6]:
{
"config": {
"broadcast": {
"self": false,
"ack": false
},
"presence": {
"key": ""
},
"postgres_changes": [
{
"schema": "public",
"table": "users",
"filter": "id=eq.1",
"event": "UPDATE"
}
]
}
}
Socket Message Received:
{"event":"presence_state","payload":{},"ref":null,"topic":"realtime:users:Lothar"}
Socket Message Received:
{"event":"phx_reply","payload":{"response":{"postgres_changes":[{"id":98019032,"event":"UPDATE","filter":"id=eq.1","schema":"public","table":"users"}]},"status":"ok"},"ref":"df8a4e15-4830-4e91-9675-5b58a70482e6","topic":"realtime:public:users"}
Socket Push [topic: realtime:public:users, event: access_token, ref: f779ebe6-fa13-4698-8be9-b9ad676e64be]:
{
"access_token": ""
}
Socket Message Received:
{"event":"presence_state","payload":{},"ref":null,"topic":"realtime:public:users"}
Socket Message Received:
{"event":"system","payload":{"channel":"public:users","extension":"postgres_changes","message":"Subscribed to PostgreSQL","status":"ok"},"ref":null,"topic":"realtime:public:users"}
Socket Push [topic: realtime:users:grimlothar, event: phx_join, ref: 70d64e2c-0f1e-43ed-a385-7d71651ce676]:
{
"config": {
"broadcast": {
"self": false,
"ack": true
},
"presence": {
"key": ""
},
"postgres_changes": []
}
}
Socket Message Received:
{"event":"phx_reply","payload":{"response":{"postgres_changes":[]},"status":"ok"},"ref":"70d64e2c-0f1e-43ed-a385-7d71651ce676","topic":"realtime:users:grimlothar"}
Socket Push [topic: realtime:users:grimlothar, event: access_token, ref: eb9393a6-dbf7-4553-85fc-267830438a55]:
{
"access_token": ""
}
Socket Message Received:
{"event":"presence_state","payload":{},"ref":null,"topic":"realtime:users:grimlothar"}
Socket Push [topic: realtime:users:grimlothar, event: broadcast, ref: 5862759f-cd8a-4760-bf15-1cde89390464]:
{
"payload": {
"matchRequestFrom": "Lothar",
"matchRequestKey": "Lothar-grimlothar"
},
"event": "matchRequest"
}
Socket Message Received:
{"event":"phx_reply","payload":{"response":{},"status":"ok"},"ref":"5862759f-cd8a-4760-bf15-1cde89390464","topic":"realtime:users:grimlothar"}
Socket Push [topic: phoenix, event: heartbeat, ref: e54140ef-7b04-44a7-9025-4995aefa1cfc]:
{}
Socket Message Received:
{"event":"phx_reply","payload":{"response":{},"status":"ok"},"ref":"e54140ef-7b04-44a7-9025-4995aefa1cfc","topic":"phoenix"}
Socket Push [topic: realtime:users:Lothar, event: access_token, ref: 840bcfa0-60f2-4e8d-9e00-5132461bd0c2]:
{
"access_token": ""
}
Socket Push [topic: realtime:public:users, event: access_token, ref: 486e0d33-3c4f-4c43-99ef-0d77f774ab24]:
{
"access_token": ""
}
Socket Push [topic: realtime:users:grimlothar, event: access_token, ref: 4c4d4632-2f4f-46cf-9040-5a943495d754]:
{
"access_token": ""
}
Socket Message Received:
{"event":"broadcast","payload":{"event":"matchRequest","payload":{"matchRequestFrom":"grimlothar","matchRequestKey":"Lothar-grimlothar"}},"ref":null,"topic":"realtime:users:Lothar"}
Socket Disconnection: Lost
UnityEngine.Debug:Log (object)
SupabaseManager/<>c:<Start>b__8_0 (object,string,System.Exception) (at Assets/SupabaseManager.cs:56)
Supabase.Realtime.Debugger:Log (object,string,System.Exception)
Supabase.Realtime.RealtimeSocket:HandleSocketDisconnectionHappened (Websocket.Client.DisconnectionInfo)
System.Reactive.Subjects.Subject`1<Websocket.Client.DisconnectionInfo>:OnNext (Websocket.Client.DisconnectionInfo)
Websocket.Client.WebsocketClient/<Reconnect>d__81:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:Start<Websocket.Client.WebsocketClient/<Reconnect>d__81> (Websocket.Client.WebsocketClient/<Reconnect>d__81&)
Websocket.Client.WebsocketClient:Reconnect (Websocket.Client.ReconnectionType,bool,System.Exception)
Websocket.Client.WebsocketClient/<ReconnectSynchronized>d__80:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:Start<Websocket.Client.WebsocketClient/<ReconnectSynchronized>d__80> (Websocket.Client.WebsocketClient/<ReconnectSynchronized>d__80&)
Websocket.Client.WebsocketClient:ReconnectSynchronized (Websocket.Client.ReconnectionType,bool,System.Exception)
Websocket.Client.WebsocketClient/<Listen>d__70:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int>:SetResult (int)
Mono.Net.Security.MobileAuthenticatedStream/<StartOperation>d__57:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Mono.Net.Security.AsyncProtocolResult>:SetResult (Mono.Net.Security.AsyncProtocolResult)
Mono.Net.Security.AsyncProtocolRequest/<StartOperation>d__23:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetResult ()
Mono.Net.Security.AsyncProtocolRequest/<ProcessOperation>d__24:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<System.Nullable`1<int>>:SetResult (System.Nullable`1<int>)
Mono.Net.Security.AsyncProtocolRequest/<InnerRead>d__25:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int>:SetResult (int)
Mono.Net.Security.MobileAuthenticatedStream/<InnerRead>d__66:MoveNext ()
System.Threading._ThreadPoolWaitCallback:PerformWaitCallback ()
Next reconnection attempt will occur at: 9/5/2023 11:17:25 AM
UnityEngine.Debug:Log (object)
SupabaseManager/<>c:<Start>b__8_0 (object,string,System.Exception) (at Assets/SupabaseManager.cs:56)
Supabase.Realtime.Debugger:Log (object,string,System.Exception)
Supabase.Realtime.RealtimeSocket:HandleSocketError (Websocket.Client.DisconnectionInfo)
Supabase.Realtime.RealtimeSocket:HandleSocketDisconnectionHappened (Websocket.Client.DisconnectionInfo)
System.Reactive.Subjects.Subject`1<Websocket.Client.DisconnectionInfo>:OnNext (Websocket.Client.DisconnectionInfo)
Websocket.Client.WebsocketClient/<Reconnect>d__81:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:Start<Websocket.Client.WebsocketClient/<Reconnect>d__81> (Websocket.Client.WebsocketClient/<Reconnect>d__81&)
Websocket.Client.WebsocketClient:Reconnect (Websocket.Client.ReconnectionType,bool,System.Exception)
Websocket.Client.WebsocketClient/<ReconnectSynchronized>d__80:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:Start<Websocket.Client.WebsocketClient/<ReconnectSynchronized>d__80> (Websocket.Client.WebsocketClient/<ReconnectSynchronized>d__80&)
Websocket.Client.WebsocketClient:ReconnectSynchronized (Websocket.Client.ReconnectionType,bool,System.Exception)
Websocket.Client.WebsocketClient/<Listen>d__70:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int>:SetResult (int)
Mono.Net.Security.MobileAuthenticatedStream/<StartOperation>d__57:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Mono.Net.Security.AsyncProtocolResult>:SetResult (Mono.Net.Security.AsyncProtocolResult)
Mono.Net.Security.AsyncProtocolRequest/<StartOperation>d__23:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetResult ()
Mono.Net.Security.AsyncProtocolRequest/<ProcessOperation>d__24:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<System.Nullable`1<int>>:SetResult (System.Nullable`1<int>)
Mono.Net.Security.AsyncProtocolRequest/<InnerRead>d__25:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int>:SetResult (int)
Mono.Net.Security.MobileAuthenticatedStream/<InnerRead>d__66:MoveNext ()
System.Threading._ThreadPoolWaitCallback:PerformWaitCallback ()
Socket Reconnection: Lost
UnityEngine.Debug:Log (object)
SupabaseManager/<>c:<Start>b__8_0 (object,string,System.Exception) (at Assets/SupabaseManager.cs:56)
Supabase.Realtime.Debugger:Log (object,string,System.Exception)
Supabase.Realtime.RealtimeSocket:HandleSocketReconnectionHappened (Websocket.Client.Models.ReconnectionInfo)
System.Reactive.Subjects.Subject`1<Websocket.Client.Models.ReconnectionInfo>:OnNext (Websocket.Client.Models.ReconnectionInfo)
Websocket.Client.WebsocketClient/<StartClient>d__68:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<System.Net.WebSockets.WebSocket>:SetResult (System.Net.WebSockets.WebSocket)
Websocket.Client.WebsocketClient/<>c/<<-ctor>b__17_0>d:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int>:SetResult (int)
Mono.Net.Security.MobileAuthenticatedStream/<StartOperation>d__57:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Mono.Net.Security.AsyncProtocolResult>:SetResult (Mono.Net.Security.AsyncProtocolResult)
Mono.Net.Security.AsyncProtocolRequest/<StartOperation>d__23:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetResult ()
Mono.Net.Security.AsyncProtocolRequest/<ProcessOperation>d__24:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<System.Nullable`1<int>>:SetResult (System.Nullable`1<int>)
Mono.Net.Security.AsyncProtocolRequest/<InnerRead>d__25:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int>:SetResult (int)
Mono.Net.Security.MobileAuthenticatedStream/<InnerRead>d__66:MoveNext ()
System.Threading._ThreadPoolWaitCallback:PerformWaitCallback ()
Socket State Change: Reconnect
UnityEngine.Debug:Log (object)
SupabaseManager/<>c:<Start>b__8_0 (object,string,System.Exception) (at Assets/SupabaseManager.cs:56)
Supabase.Realtime.Debugger:Log (object,string,System.Exception)
Supabase.Realtime.RealtimeSocket:NotifySocketStateChange (Supabase.Realtime.Constants/SocketState)
Supabase.Realtime.RealtimeSocket:HandleSocketOpened ()
Supabase.Realtime.RealtimeSocket:HandleSocketReconnectionHappened (Websocket.Client.Models.ReconnectionInfo)
System.Reactive.Subjects.Subject`1<Websocket.Client.Models.ReconnectionInfo>:OnNext (Websocket.Client.Models.ReconnectionInfo)
Websocket.Client.WebsocketClient/<StartClient>d__68:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<System.Net.WebSockets.WebSocket>:SetResult (System.Net.WebSockets.WebSocket)
Websocket.Client.WebsocketClient/<>c/<<-ctor>b__17_0>d:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int>:SetResult (int)
Mono.Net.Security.MobileAuthenticatedStream/<StartOperation>d__57:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<Mono.Net.Security.AsyncProtocolResult>:SetResult (Mono.Net.Security.AsyncProtocolResult)
Mono.Net.Security.AsyncProtocolRequest/<StartOperation>d__23:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder:SetResult ()
Mono.Net.Security.AsyncProtocolRequest/<ProcessOperation>d__24:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<System.Nullable`1<int>>:SetResult (System.Nullable`1<int>)
Mono.Net.Security.AsyncProtocolRequest/<InnerRead>d__25:MoveNext ()
System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1<int>:SetResult (int)
Mono.Net.Security.MobileAuthenticatedStream/<InnerRead>d__66:MoveNext ()
System.Threading._ThreadPoolWaitCallback:PerformWaitCallback ()
Socket Push [topic: realtime:users:Lothar, event: phx_join, ref: 4b38206a-c416-4d65-9277-1e1cc1ce4e2d]:
{
"config": {
"broadcast": {
"self": false,
"ack": true
},
"presence": {
"key": ""
},
"postgres_changes": []
}
}
Socket Push [topic: realtime:public:users, event: phx_join, ref: 6ca43d30-cca7-4a61-809e-804159576c46]:
{
"config": {
"broadcast": {
"self": false,
"ack": false
},
"presence": {
"key": ""
},
"postgres_changes": [
{
"schema": "public",
"table": "users",
"filter": "id=eq.1",
"event": "UPDATE"
}
]
}
}
Socket Push [topic: realtime:users:grimlothar, event: phx_join, ref: 9329b77d-15fd-43bd-8bd8-1e01caa85967]:
{
"config": {
"broadcast": {
"self": false,
"ack": true
},
"presence": {
"key": ""
},
"postgres_changes": []
}
}
Socket Push [topic: phoenix, event: heartbeat, ref: 86d0585b-e7c6-462d-b50a-f9d2707c018b]:
{}
Channel not joined. Try to rejoin?