diff --git a/CHANGELOG.md b/CHANGELOG.md index b8e164db..a8286b9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - Fixed rate limiter rules matching to acount for the `Audience` field. +- Force closing the realtime connection on unregistering a client (aka. on `app.SubscriptionsBroker().Unregister(clientId)`) + - Minor UI fixes (fixed duplicate record control, removed duplicated id field in the record preview, hide Impersonate button for non-auth records, auto sort rate limit rules, etc.). diff --git a/tools/subscriptions/broker.go b/tools/subscriptions/broker.go index 21a4eb85..f2675eb4 100644 --- a/tools/subscriptions/broker.go +++ b/tools/subscriptions/broker.go @@ -52,7 +52,7 @@ func (b *Broker) Register(client Client) { b.store.Set(client.Id(), client) } -// Unregister removes a single client by its id. +// Unregister removes a single client by its id and marks it as discarded. // // If client with clientId doesn't exist, this method does nothing. func (b *Broker) Unregister(clientId string) { diff --git a/tools/subscriptions/client.go b/tools/subscriptions/client.go index d4d3430e..0bcb3594 100644 --- a/tools/subscriptions/client.go +++ b/tools/subscriptions/client.go @@ -32,6 +32,8 @@ type Client interface { Id() string // Channel returns the client's communication channel. + // + // NB! The channel shouldn't be used after calling Discard(). Channel() chan Message // Subscriptions returns a shallow copy of the client subscriptions matching the prefixes. @@ -65,8 +67,8 @@ type Client interface { // Get retrieves the key value from the client's context. Get(key string) any - // Discard marks the client as "discarded", meaning that it - // shouldn't be used anymore for sending new messages. + // Discard marks the client as "discarded" (and closes its channel), + // meaning that it shouldn't be used anymore for sending new messages. // // It is safe to call Discard() multiple times. Discard() @@ -257,7 +259,13 @@ func (c *DefaultClient) Discard() { c.mux.Lock() defer c.mux.Unlock() + if c.isDiscarded { + return + } + c.isDiscarded = true + + close(c.channel) } // IsDiscarded implements the [Client.IsDiscarded] interface method.