Releases: centrifugal/centrifuge
v0.16.0
This release is huge. The list of changes may look scary - but most changes should be pretty straightforward to adopt.
Highlights:
- Support for unidirectional clients, this opens a road to more adoption of Centrifuge for cases where bidirectional communication is not really needed. Unidirectional support is still a subject to change in future versions as soon as more feedback appears – for now Centrifuge has examples for GRPC, EventSource(SSE), Fetch Streams, Unidirectional WebSocket transports. The beauty here is that you don't need to use any Centrifuge client library to receive real-time updates - just use native browser APIs or GRPC generated code with simple decoding step.
- The introduction of unidirectional transport required to change
Transport
interface a bit. The important thing is that it's now a responsibility ofTransport.Write
to properly encode data to JSON-streaming or Protobuf length-delimited format - Centrifuge now uses same-origin policy by default when checking incoming WebSocket or SockJS request due to security considerations (prevent CSRF attack), previously default check allowed all connections. If you want to mimic previous behavior then pass custom check functions to handler configurations – see example below.
- New
Subscribe
method ofNode
- to subscribe user to server-side channels cluster-wide - see example that demonstrates new API - Engine interface removed - now Centrifuge only has separate
Broker
andPresenceManager
entities. This changes how you should set up Redis to scale Nodes - see updated Redis example - it's now required to provide Redis Broker and Redis Presence Manager separately - Trace level logging (to see all protocol frames, obviously mostly suitable for development)
WithResubscribe
option of unsubscribe removed - it never worked properly in client libraries and had no clearly defined semantics- It is now possible to return custom data in Subscribe result or in Subscribe Push
Broker.PublishControl
method signature changed - it now acceptsshardKey
string argument, this is not used at the moment but can be used later if we will need an order of control messagesPresenceManager.AddPresence
signature changed - now presence expiration time is an option of PresenceManager itself- Field
version
ofConnectResult
is now omitted from JSON if empty - Server-side subscriptions now trigger unsubscribe event (with
ServerSide
boolean flag set totrue
) - Slightly faster Redis history decoding - commit
- Hub container now shards connections and subscriptions - this can reduce lock contention significantly in some workloads thus reducing operation latency. See #184
- Various example improvements
That's what you have to do if you want to accept all connections without same-origin check introduced in v0.16.0 (can lead to CSRF vulnerability in some situations):
wsHandler := centrifuge.NewWebsocketHandler(node, centrifuge.WebsocketConfig{
CheckOrigin: func(r *http.Request) bool {
return true
},
})
sockjsHandler := centrifuge.NewSockjsHandler(node, centrifuge.SockjsConfig{
CheckOrigin: func(r *http.Request) bool {
return true
},
WebsocketCheckOrigin: func(r *http.Request) bool {
return true
},
})
All changes:
$ gorelease -base v0.15.0 -version v0.16.0
github.com/centrifugal/centrifuge
---------------------------------
Incompatible changes:
- (*Client).Subscribe: changed from func(string) error to func(string, ...SubscribeOption) error
- (*Client).Unsubscribe: changed from func(string, ...UnsubscribeOption) error to func(string) error
- (*Node).SetEngine: removed
- Broker.PublishControl: changed from func([]byte, string) error to func([]byte, string, string) error
- Config.ClientPresenceExpireInterval: removed
- Engine: removed
- LogLevelDebug: value changed from 1 to 2
- LogLevelError: value changed from 3 to 4
- LogLevelInfo: value changed from 2 to 3
- MemoryEngine: removed
- MemoryEngineConfig: removed
- NewMemoryEngine: removed
- NewRedisEngine: removed
- PresenceManager.AddPresence: changed from func(string, string, *ClientInfo, time.Duration) error to func(string, string, *ClientInfo) error
- RedisEngine: removed
- RedisEngineConfig: removed
- RedisShardConfig.ClusterAddrs: removed
- RedisShardConfig.Host: removed
- RedisShardConfig.Port: removed
- RedisShardConfig.Prefix: removed
- RedisShardConfig.PubSubNumWorkers: removed
- RedisShardConfig.SentinelAddrs: removed
- Transport.Write: changed from func([]byte) error to func(...[]byte) error
- TransportInfo.DisabledPushFlags: added
- TransportInfo.Unidirectional: added
- UnsubscribeOptions.Resubscribe: removed
- WithResubscribe: removed
Compatible changes:
- (*Client).Connect: added
- (*Node).Subscribe: added
- ConnectRequest: added
- DefaultRedisBrokerPrefix: added
- DefaultRedisConnectTimeout: added
- DefaultRedisPresenceManagerPrefix: added
- DefaultRedisPresenceTTL: added
- DefaultRedisReadTimeout: added
- DefaultRedisWriteTimeout: added
- LogLevelTrace: added
- MemoryBroker: added
- MemoryBrokerConfig: added
- MemoryPresenceManager: added
- MemoryPresenceManagerConfig: added
- NewMemoryBroker: added
- NewMemoryPresenceManager: added
- NewRedisBroker: added
- NewRedisPresenceManager: added
- NewRedisShard: added
- PushFlagConnect: added
- PushFlagDisconnect: added
- PushFlagJoin: added
- PushFlagLeave: added
- PushFlagMessage: added
- PushFlagPublication: added
- PushFlagSubscribe: added
- PushFlagUnsubscribe: added
- RedisBroker: added
- RedisBrokerConfig: added
- RedisPresenceManager: added
- RedisPresenceManagerConfig: added
- RedisShard: added
- RedisShardConfig.Address: added
- RedisShardConfig.ClusterAddresses: added
- RedisShardConfig.SentinelAddresses: added
- SubscribeOption: added
- SubscribeOptions.Data: added
- SubscribeRequest: added
- UnsubscribeEvent.ServerSide: added
- WebsocketConfig.Unidirectional: added
- WithChannelInfo: added
- WithExpireAt: added
- WithJoinLeave: added
- WithPosition: added
- WithPresence: added
- WithRecover: added
- WithSubscribeClient: added
- WithSubscribeData: added
- WithUnsubscribeClient: added
v0.16.0 is a valid semantic version for this release.
v0.15.0
- Add
Node.Survey
method – it allows gathering results from all running nodes. It's possible to define your own survey handlers. See example. Keep in mind thatSurvey
does not scale very well as number of Centrifuge Nodes grows. Though it has reasonably good performance to perform rare tasks even with relatively large number of nodes – see benchmark in pull request - The main motivation of adding
Node.Survey
was attempt to removeBroker.Channels
method – which is not supported by most of existing PUB/SUB brokers and does not work in Redis cluster.Broker.Channels
now removed, but it can be replaced with survey if needed - Improve clustering - node will now send a SHUTDOWN message so other nodes have a chance to realize that node left cluster almost immediately
- Signature of
Since
history call option changed – it now accepts a pointer to StreamPosition. This change simplifies a code to construct history call - Added
SubscribeOptions.Position
boolean flag to enable positioning in channel stream. Positioning means that Centrifuge will check that the client did not miss any message from PUB/SUB system, as soon as loss detected client will be disconnected withInsufficient State
reason. This is very similar to whatRecover: true
option did, butPosition: true
does not enable recovery. As soon asPosition
flag enabled Centrifuge will expose top streamStreamPosition
information to a client in Subscribe Reply - Added possibility to iterate over a channel history stream from client side. See an example that demonstrates this
- New
Config
options:HistoryMaxPublicationLimit
andRecoveryMaxPublicationLimit
to control maximum number of publications to return during history call or recovery process. See Centrifuge documentation for detailed description - New example that shows Centrifuge integration with Tarantool. Tarantool engine implementation can outperform Redis (up to 5-10x for presence and history operations), though while example contains a full-featured fast Engine implementation – it's still just an example at the moment and have not been tested in production environment
- New blog post in Centrifugo blog where we introduce Centrifuge library
- Most examples now do not use jQuery which was replaced by vanilla JS
$ gorelease -base v0.14.2 -version v0.15.0
github.com/centrifugal/centrifuge
---------------------------------
Incompatible changes:
- (*MemoryEngine).Channels: removed
- (*MemoryEngine).PublishControl: changed from func([]byte) error to func([]byte, string) error
- (*Node).Channels: removed
- (*RedisEngine).Channels: removed
- (*RedisEngine).PublishControl: changed from func([]byte) error to func([]byte, string) error
- Broker.Channels, method set of Engine: removed
- Broker.Channels: removed
- Broker.PublishControl: changed from func([]byte) error to func([]byte, string) error
- BrokerEventHandler.HandlePublication: changed from func(string, *Publication) error to func(string, *Publication, StreamPosition) error
- Since: changed from func(StreamPosition) HistoryOption to func(*StreamPosition) HistoryOption
Compatible changes:
- (*Node).ID: added
- (*Node).OnSurvey: added
- (*Node).Survey: added
- Config.HistoryMaxPublicationLimit: added
- Config.RecoveryMaxPublicationLimit: added
- ErrorUnrecoverablePosition: added
- HistoryEvent.Filter: added
- SubscribeOptions.Position: added
- SurveyCallback: added
- SurveyEvent: added
- SurveyHandler: added
- SurveyReply: added
- SurveyResult: added
v0.15.0 is a valid semantic version for this release.
v0.14.2
v0.14.1
v0.14.0
- Add possibility to disconnect user with custom
Disconnect
object, and with client ID whitelist. - Thus fixing non-working
WithReconnect
option when callingnode.Disconnect
method. - No error returned from
client.Disconnect
method anymore. It was alwaysnil
before.
Here is what changed since v0.13.0:
gorelease -base v0.13.0 -version v0.14.0
github.com/centrifugal/centrifuge
---------------------------------
Incompatible changes:
- (*Client).Disconnect: changed from func(*Disconnect) error to func(*Disconnect)
- DisconnectOptions.Reconnect: removed
- DisconnectOptions: old is comparable, new is not
- WithReconnect: removed
Compatible changes:
- DisconnectOptions.ClientWhitelist: added
- DisconnectOptions.Disconnect: added
- WithClientWhitelist: added
- WithDisconnect: added
v0.14.0 is a valid semantic version for this release.
v0.13.0
This release solves two important issues from v1.0.0 library milestone. It has API changes, though as always it's possible to implement the same as before, and adapting new version should be pretty straightforward.
- #163 Provide a way to add concurrent processing of protocol commands. Before this change protocol commands could only be processed one by one. The obvious drawback in this case is that one slow RPC could result into stopping other requests from being processed thus affecting overall latency. This required changing client handler API and use asynchronous callback style API for returning replies from event handlers. This approach while not being very idiomatic allows using whatever concurrency strategy developer wants without losing the possibility to control event order.
- #161 Eliminating
ChannelOptionsFunc
– now all channel options can be provided when callingPublish
operation (history size and TTL) or by returning from event handlers insideSubscribeReply
(enabling channel presence, join/leave messages, recovery in a channel). This means that channel options can now be controlled per-connection (not only per channel as before). For example if you need admin connection to subscribe to channel but not participate in channel presence – you are able to not enable presence for that connection. - Server-side subscriptions now set over
Subscriptions
map (instead ofChannels
). Again – subscribe options can be set with per-connection resolution. - Change signature of
Publish
method inBroker
interface – method now accepts[]byte
data instead of*Publication
. - Function options for
Unsubbscribe
andDisconnect
methods now have boolean argument. - History functional option
WithNoLimit
removed – useWithLimit(centrifuge.NoLimit)
instead. - Config option
ClientUserConnectionLimit
renamed toUserConnectionLimit
. IfUserConnectionLimit
set then now connection will be disconnected withDisconnectConnectionLimit
instead of returning aLimitExceeded
error.
Since API changes are pretty big, let's look at example program and how to adapt it from v0.12.0 to v0.13.0.
The program based on v0.12.0 API:
package main
import (
"context"
"github.com/centrifugal/centrifuge"
)
func main() {
cfg := centrifuge.DefaultConfig
cfg.ChannelOptionsFunc = func(channel string) (centrifuge.ChannelOptions, bool, error) {
return centrifuge.ChannelOptions{
Presence: true,
JoinLeave: true,
HistorySize: 100,
HistoryLifetime: 300,
HistoryRecover: true,
}, true, nil
}
node, _ := centrifuge.New(cfg)
node.OnConnecting(func(ctx context.Context, e centrifuge.ConnectEvent) (centrifuge.ConnectReply, error) {
return centrifuge.ConnectReply{
Credentials: ¢rifuge.Credentials{UserID: "42"},
// Subscribe to a server-side channel.
Channels: []string{"news"},
}, nil
})
node.OnConnect(func(c *centrifuge.Client) {
println("client connected")
})
node.OnSubscribe(func(c *centrifuge.Client, e centrifuge.SubscribeEvent) (centrifuge.SubscribeReply, error) {
return centrifuge.SubscribeReply{}, nil
})
node.OnPublish(func(c *centrifuge.Client, e centrifuge.PublishEvent) (centrifuge.PublishReply, error) {
return centrifuge.PublishReply{}, nil
})
node.OnDisconnect(func(c *centrifuge.Client, e centrifuge.DisconnectEvent) {
println("client disconnected")
})
_ = node.Run()
}
With v0.13.0 the same program becomes:
package main
import (
"context"
"time"
"github.com/centrifugal/centrifuge"
)
func main() {
node, _ := centrifuge.New(centrifuge.DefaultConfig)
node.OnConnecting(func(ctx context.Context, e centrifuge.ConnectEvent) (centrifuge.ConnectReply, error) {
return centrifuge.ConnectReply{
Credentials: ¢rifuge.Credentials{UserID: "42"},
// Subscribe to a server-side channel.
Subscriptions: map[string]centrifuge.SubscribeOptions{
"news": {Presence: true, JoinLeave: true, Recover: true},
},
}, nil
})
node.OnConnect(func(client *centrifuge.Client) {
println("client connected")
client.OnSubscribe(func(e centrifuge.SubscribeEvent, cb centrifuge.SubscribeCallback) {
cb(centrifuge.SubscribeReply{
Options: centrifuge.SubscribeOptions{
Presence: true,
JoinLeave: true,
Recover: true,
},
}, nil)
})
client.OnPublish(func(e centrifuge.PublishEvent, cb centrifuge.PublishCallback) {
// BTW you can publish here explicitly using node.Publish method – see Result
// field of PublishReply and chat_json example.
cb(centrifuge.PublishReply{
Options: centrifuge.PublishOptions{
HistorySize: 100,
HistoryTTL: 5 * time.Minute,
},
}, nil)
})
client.OnDisconnect(func(e centrifuge.DisconnectEvent) {
println("client disconnected")
})
})
_ = node.Run()
}
As you can see there are three important changes:
- You should now set up event handlers inside
node.OnConnect
closure - Event handlers now have callback argument that you should call with corresponding Reply as soon as you have it
- For server-side subscriptions you should now return
Subscriptions
field inConnectReply
which ismap[string]SubscribeOptions
instead of[]string
slice.
See new example that demonstrates concurrency using bounded semaphore.
Note that every feature enabled for a channel increases resource usage on a server. You should only enable presence, recovery, join/leave features and maintaining history in channels where this is necessary.
See also updated Tips and tricks section in a README – it now contains information about connection life cycle and event handler concurrency.
v0.12.0
This release is a step back in Engine separation and has some important fixes and improvements. Backwards incompatible changes are all about Engine interfaces so if you are using built-in Memory or Redis engines you should be fine to upgrade. Otherwise, take a closer look on first and second points below.
HistoryManager
interface removed and its methods now part ofBroker
interface{}. The reason behind this is that Broker should be responsible for an atomicity of saving message into history stream and publish to PUB/SUB. More details in #158- Cleaner
Broker
interface methods withoutChannelOptions
- Fix reconnects due to
InsufficientState
errors in channels withHistoryRecover
option on when using Memory Engine and frequently publishing in parallel (from different goroutines) - Fix reconnects due to
InsufficientState
errors when using legacy seq, gen fields - #157 - Fix returning custom disconnect for SockJS transport
- Possibility to define history stream options in
Publish
call - Deprecate Broker/Engine
Channels
method – see #147 - Increase test coverage up to 83% so #106 is finally closed
- Test Sentinel scenario in CI
- Refactor queue writer to prevent possible message loss on connection close - 160
- Fix inconsistent tests of Redis Cluster recovery due to PUB/SUB buffering
- Minor improvements in Gin auth example - #154
I have a plan for future library versions to remove ChannelOptionFunc
completely (but still have a control over channel feature set). This is still in research – if you are interested welcome to #161.
$ gorelease -base v0.11.2 -version v0.12.0
github.com/centrifugal/centrifuge
---------------------------------
Incompatible changes:
- (*MemoryEngine).AddHistory: removed
- (*MemoryEngine).Publish: changed from func(string, *Publication, *ChannelOptions) error to func(string, *Publication, PublishOptions) (StreamPosition, error)
- (*MemoryEngine).PublishJoin: changed from func(string, *ClientInfo, *ChannelOptions) error to func(string, *ClientInfo) error
- (*MemoryEngine).PublishLeave: changed from func(string, *ClientInfo, *ChannelOptions) error to func(string, *ClientInfo) error
- (*Node).SetHistoryManager: removed
- (*RedisEngine).AddHistory: removed
- (*RedisEngine).Publish: changed from func(string, *Publication, *ChannelOptions) error to func(string, *Publication, PublishOptions) (StreamPosition, error)
- (*RedisEngine).PublishJoin: changed from func(string, *ClientInfo, *ChannelOptions) error to func(string, *ClientInfo) error
- (*RedisEngine).PublishLeave: changed from func(string, *ClientInfo, *ChannelOptions) error to func(string, *ClientInfo) error
- Broker.History: added
- Broker.Publish: changed from func(string, *Publication, *ChannelOptions) error to func(string, *Publication, PublishOptions) (StreamPosition, error)
- Broker.PublishJoin: changed from func(string, *ClientInfo, *ChannelOptions) error to func(string, *ClientInfo) error
- Broker.PublishLeave: changed from func(string, *ClientInfo, *ChannelOptions) error to func(string, *ClientInfo) error
- Broker.RemoveHistory: added
- HistoryManager.AddHistory, method set of Engine: removed
- HistoryManager: removed
- MemoryEngine: old is comparable, new is not
- PublishOptions.SkipHistory: removed
- RedisEngineConfig.PublishOnHistoryAdd: removed
Compatible changes:
- PublishOptions.HistorySize: added
- PublishOptions.HistoryTTL: added
- WithHistory: added
v0.12.0 is a valid semantic version for this release.
v0.11.2
v0.11.1
- Added
MetricsNamespace
field ofConfig
to configure Prometheus metrics namespace used by Centrifuge library internal metrics - Fix
messages_sent_counter
– it now correctly counts Control, Join and Leave messages - Redis cluster integration now tested in CI
$ gorelease -base v0.11.0 -version v0.11.1
github.com/centrifugal/centrifuge
---------------------------------
Compatible changes:
- Config.MetricsNamespace: added
v0.11.1 is a valid semantic version for this release.
v0.11.0
- Refactor client channels API – see detailed changes below, #146
- Fix atomic alignment in struct for 32-bit builds, commit
- Field
Code
ofDisconnect
hasuint32
type now instead ofint
, commit - Refactor WebSocket graceful close – do not use a new goroutine for every read, #144
- Support client name and version fields of
Connect
command which will be available inConnectEvent
struct (if set on client side), #145
$ gorelease -base v0.10.1 -version v0.11.0
github.com/centrifugal/centrifuge
---------------------------------
Incompatible changes:
- (*Client).Channels: changed from func() map[string]ChannelContext to func() []string
- ChannelContext: removed
- Disconnect.Code: changed from int to uint32
Compatible changes:
- (*Client).IsSubscribed: added
- ConnectEvent.Name: added
- ConnectEvent.Version: added
- ErrorTooManyRequests: added
v0.11.0 is a valid semantic version for this release.