1
0
mirror of https://github.com/redis/go-redis.git synced 2025-07-29 17:41:15 +03:00

feat: add general push notification system

- Add PushNotificationRegistry for managing notification handlers
- Add PushNotificationProcessor for processing RESP3 push notifications
- Add client methods for registering push notification handlers
- Add PubSub integration for handling generic push notifications
- Add comprehensive test suite with 100% coverage
- Add push notification demo example

This system allows handling any arbitrary RESP3 push notification
with registered handlers, not just specific notification types.
This commit is contained in:
Nedyalko Dyakov
2025-06-26 20:26:02 +03:00
parent 0decfdc6ed
commit b02eed63b2
6 changed files with 1633 additions and 2 deletions

View File

@ -38,12 +38,21 @@ type PubSub struct {
chOnce sync.Once
msgCh *channel
allCh *channel
// Push notification processor for handling generic push notifications
pushProcessor *PushNotificationProcessor
}
func (c *PubSub) init() {
c.exit = make(chan struct{})
}
// SetPushNotificationProcessor sets the push notification processor for handling
// generic push notifications received on this PubSub connection.
func (c *PubSub) SetPushNotificationProcessor(processor *PushNotificationProcessor) {
c.pushProcessor = processor
}
func (c *PubSub) String() string {
c.mu.Lock()
defer c.mu.Unlock()
@ -367,6 +376,18 @@ func (p *Pong) String() string {
return "Pong"
}
// PushNotificationMessage represents a generic push notification received on a PubSub connection.
type PushNotificationMessage struct {
// Command is the push notification command (e.g., "MOVING", "CUSTOM_EVENT").
Command string
// Args are the arguments following the command.
Args []interface{}
}
func (m *PushNotificationMessage) String() string {
return fmt.Sprintf("push: %s", m.Command)
}
func (c *PubSub) newMessage(reply interface{}) (interface{}, error) {
switch reply := reply.(type) {
case string:
@ -413,6 +434,18 @@ func (c *PubSub) newMessage(reply interface{}) (interface{}, error) {
Payload: reply[1].(string),
}, nil
default:
// Try to handle as generic push notification
if c.pushProcessor != nil && c.pushProcessor.IsEnabled() {
ctx := c.getContext()
handled := c.pushProcessor.GetRegistry().HandleNotification(ctx, reply)
if handled {
// Return a special message type to indicate it was handled
return &PushNotificationMessage{
Command: kind,
Args: reply[1:],
}, nil
}
}
return nil, fmt.Errorf("redis: unsupported pubsub message: %q", kind)
}
default:
@ -658,6 +691,9 @@ func (c *channel) initMsgChan() {
// Ignore.
case *Pong:
// Ignore.
case *PushNotificationMessage:
// Ignore push notifications in message-only channel
// They are already handled by the push notification processor
case *Message:
timer.Reset(c.chanSendTimeout)
select {
@ -712,7 +748,7 @@ func (c *channel) initAllChan() {
switch msg := msg.(type) {
case *Pong:
// Ignore.
case *Subscription, *Message:
case *Subscription, *Message, *PushNotificationMessage:
timer.Reset(c.chanSendTimeout)
select {
case c.allCh <- msg: