Skip to content

Commit 84123b1

Browse files
committed
refactor(push): completly change the package structure
1 parent b4d0ff1 commit 84123b1

13 files changed

+1982
-389
lines changed

options.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
"github.com/redis/go-redis/v9/auth"
1717
"github.com/redis/go-redis/v9/internal/pool"
18+
"github.com/redis/go-redis/v9/push"
1819
)
1920

2021
// Limiter is the interface of a rate limiter or a circuit breaker.
@@ -222,7 +223,7 @@ type Options struct {
222223

223224
// PushNotificationProcessor is the processor for handling push notifications.
224225
// If nil, a default processor will be created for RESP3 connections.
225-
PushNotificationProcessor PushNotificationProcessorInterface
226+
PushNotificationProcessor push.NotificationProcessor
226227
}
227228

228229
func (opt *Options) init() {

pubsub.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/redis/go-redis/v9/internal"
1111
"github.com/redis/go-redis/v9/internal/pool"
1212
"github.com/redis/go-redis/v9/internal/proto"
13+
"github.com/redis/go-redis/v9/push"
1314
)
1415

1516
// PubSub implements Pub/Sub commands as described in
@@ -40,7 +41,7 @@ type PubSub struct {
4041
allCh *channel
4142

4243
// Push notification processor for handling generic push notifications
43-
pushProcessor PushNotificationProcessorInterface
44+
pushProcessor push.NotificationProcessor
4445
}
4546

4647
func (c *PubSub) init() {
@@ -551,14 +552,13 @@ func (c *PubSub) processPendingPushNotificationWithReader(ctx context.Context, c
551552
return c.pushProcessor.ProcessPendingNotifications(ctx, handlerCtx, rd)
552553
}
553554

554-
func (c *PubSub) pushNotificationHandlerContext(cn *pool.Conn) PushNotificationHandlerContext {
555+
func (c *PubSub) pushNotificationHandlerContext(cn *pool.Conn) push.NotificationHandlerContext {
555556
// PubSub doesn't have a client or connection pool, so we pass nil for those
556557
// PubSub connections are blocking
557-
return NewPushNotificationHandlerContext(nil, nil, c, cn, true)
558+
return push.HandlerContext{}
559+
return push.NewNotificationHandlerContext(nil, nil, c, cn, true)
558560
}
559561

560-
561-
562562
type ChannelOption func(c *channel)
563563

564564
// WithChannelSize specifies the Go chan size that is used to buffer incoming messages.

push/errors.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package push
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strings"
7+
)
8+
9+
// Push notification error definitions
10+
// This file contains all error types and messages used by the push notification system
11+
12+
// Common error variables for reuse
13+
var (
14+
// ErrHandlerNil is returned when attempting to register a nil handler
15+
ErrHandlerNil = errors.New("handler cannot be nil")
16+
)
17+
18+
// Registry errors
19+
20+
// ErrHandlerExists creates an error for when attempting to overwrite an existing handler
21+
func ErrHandlerExists(pushNotificationName string) error {
22+
return fmt.Errorf("cannot overwrite existing handler for push notification: %s", pushNotificationName)
23+
}
24+
25+
// ErrProtectedHandler creates an error for when attempting to unregister a protected handler
26+
func ErrProtectedHandler(pushNotificationName string) error {
27+
return fmt.Errorf("cannot unregister protected handler for push notification: %s", pushNotificationName)
28+
}
29+
30+
// VoidProcessor errors
31+
32+
// ErrVoidProcessorRegister creates an error for when attempting to register a handler on void processor
33+
func ErrVoidProcessorRegister(pushNotificationName string) error {
34+
return fmt.Errorf("cannot register push notification handler '%s': push notifications are disabled (using void processor)", pushNotificationName)
35+
}
36+
37+
// ErrVoidProcessorUnregister creates an error for when attempting to unregister a handler on void processor
38+
func ErrVoidProcessorUnregister(pushNotificationName string) error {
39+
return fmt.Errorf("cannot unregister push notification handler '%s': push notifications are disabled (using void processor)", pushNotificationName)
40+
}
41+
42+
// Error message constants for consistency
43+
const (
44+
// Error message templates
45+
MsgHandlerNil = "handler cannot be nil"
46+
MsgHandlerExists = "cannot overwrite existing handler for push notification: %s"
47+
MsgProtectedHandler = "cannot unregister protected handler for push notification: %s"
48+
MsgVoidProcessorRegister = "cannot register push notification handler '%s': push notifications are disabled (using void processor)"
49+
MsgVoidProcessorUnregister = "cannot unregister push notification handler '%s': push notifications are disabled (using void processor)"
50+
)
51+
52+
// Error type definitions for advanced error handling
53+
54+
// HandlerError represents errors related to handler operations
55+
type HandlerError struct {
56+
Operation string // "register", "unregister", "get"
57+
PushNotificationName string
58+
Reason string
59+
Err error
60+
}
61+
62+
func (e *HandlerError) Error() string {
63+
if e.Err != nil {
64+
return fmt.Sprintf("handler %s failed for '%s': %s (%v)", e.Operation, e.PushNotificationName, e.Reason, e.Err)
65+
}
66+
return fmt.Sprintf("handler %s failed for '%s': %s", e.Operation, e.PushNotificationName, e.Reason)
67+
}
68+
69+
func (e *HandlerError) Unwrap() error {
70+
return e.Err
71+
}
72+
73+
// NewHandlerError creates a new HandlerError
74+
func NewHandlerError(operation, pushNotificationName, reason string, err error) *HandlerError {
75+
return &HandlerError{
76+
Operation: operation,
77+
PushNotificationName: pushNotificationName,
78+
Reason: reason,
79+
Err: err,
80+
}
81+
}
82+
83+
// ProcessorError represents errors related to processor operations
84+
type ProcessorError struct {
85+
ProcessorType string // "processor", "void_processor"
86+
Operation string // "process", "register", "unregister"
87+
Reason string
88+
Err error
89+
}
90+
91+
func (e *ProcessorError) Error() string {
92+
if e.Err != nil {
93+
return fmt.Sprintf("%s %s failed: %s (%v)", e.ProcessorType, e.Operation, e.Reason, e.Err)
94+
}
95+
return fmt.Sprintf("%s %s failed: %s", e.ProcessorType, e.Operation, e.Reason)
96+
}
97+
98+
func (e *ProcessorError) Unwrap() error {
99+
return e.Err
100+
}
101+
102+
// NewProcessorError creates a new ProcessorError
103+
func NewProcessorError(processorType, operation, reason string, err error) *ProcessorError {
104+
return &ProcessorError{
105+
ProcessorType: processorType,
106+
Operation: operation,
107+
Reason: reason,
108+
Err: err,
109+
}
110+
}
111+
112+
// Helper functions for common error scenarios
113+
114+
// IsHandlerNilError checks if an error is due to a nil handler
115+
func IsHandlerNilError(err error) bool {
116+
return errors.Is(err, ErrHandlerNil)
117+
}
118+
119+
// IsHandlerExistsError checks if an error is due to attempting to overwrite an existing handler
120+
func IsHandlerExistsError(err error) bool {
121+
if err == nil {
122+
return false
123+
}
124+
return fmt.Sprintf("%v", err) == fmt.Sprintf(MsgHandlerExists, extractNotificationName(err))
125+
}
126+
127+
// IsProtectedHandlerError checks if an error is due to attempting to unregister a protected handler
128+
func IsProtectedHandlerError(err error) bool {
129+
if err == nil {
130+
return false
131+
}
132+
return fmt.Sprintf("%v", err) == fmt.Sprintf(MsgProtectedHandler, extractNotificationName(err))
133+
}
134+
135+
// IsVoidProcessorError checks if an error is due to void processor operations
136+
func IsVoidProcessorError(err error) bool {
137+
if err == nil {
138+
return false
139+
}
140+
errStr := err.Error()
141+
return strings.Contains(errStr, "push notifications are disabled (using void processor)")
142+
}
143+
144+
// extractNotificationName attempts to extract the notification name from error messages
145+
// This is a helper function for error type checking
146+
func extractNotificationName(err error) string {
147+
// This is a simplified implementation - in practice, you might want more sophisticated parsing
148+
// For now, we return a placeholder since the exact extraction logic depends on the error format
149+
return "unknown"
150+
}

push/handler.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package push
2+
3+
import (
4+
"context"
5+
)
6+
7+
// NotificationHandler defines the interface for push notification handlers.
8+
type NotificationHandler interface {
9+
// HandlePushNotification processes a push notification with context information.
10+
// The handlerCtx provides information about the client, connection pool, and connection
11+
// on which the notification was received, allowing handlers to make informed decisions.
12+
// Returns an error if the notification could not be handled.
13+
HandlePushNotification(ctx context.Context, handlerCtx NotificationHandlerContext, notification []interface{}) error
14+
}

push/handler_context.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package push
2+
3+
import (
4+
"github.com/redis/go-redis/v9/internal/pool"
5+
)
6+
7+
// NotificationHandlerContext provides context information about where a push notification was received.
8+
// This interface allows handlers to make informed decisions based on the source of the notification
9+
// with strongly typed access to different client types using concrete types.
10+
type NotificationHandlerContext interface {
11+
// GetClient returns the Redis client instance that received the notification.
12+
// Returns nil if no client context is available.
13+
// It is interface to both allow for future expansion and to avoid
14+
// circular dependencies. The developer is responsible for type assertion.
15+
// It can be one of the following types:
16+
// - *redis.Client
17+
// - *redis.ClusterClient
18+
// - *redis.Conn
19+
GetClient() interface{}
20+
21+
// GetConnPool returns the connection pool from which the connection was obtained.
22+
// Returns nil if no connection pool context is available.
23+
// It is interface to both allow for future expansion and to avoid
24+
// circular dependencies. The developer is responsible for type assertion.
25+
// It can be one of the following types:
26+
// - *pool.ConnPool
27+
// - *pool.SingleConnPool
28+
// - *pool.StickyConnPool
29+
GetConnPool() interface{}
30+
31+
// GetPubSub returns the PubSub instance that received the notification.
32+
// Returns nil if this is not a PubSub connection.
33+
// It is interface to both allow for future expansion and to avoid
34+
// circular dependencies. The developer is responsible for type assertion.
35+
// It can be one of the following types:
36+
// - *redis.PubSub
37+
GetPubSub() interface{}
38+
39+
// GetConn returns the specific connection on which the notification was received.
40+
// Returns nil if no connection context is available.
41+
GetConn() *pool.Conn
42+
43+
// IsBlocking returns true if the notification was received on a blocking connection.
44+
IsBlocking() bool
45+
}
46+
47+
// pushNotificationHandlerContext is the concrete implementation of PushNotificationHandlerContext interface
48+
type pushNotificationHandlerContext struct {
49+
client interface{}
50+
connPool interface{}
51+
pubSub interface{}
52+
conn *pool.Conn
53+
isBlocking bool
54+
}
55+
56+
// NewNotificationHandlerContext creates a new push.NotificationHandlerContext instance
57+
func NewNotificationHandlerContext(client, connPool, pubSub interface{}, conn *pool.Conn, isBlocking bool) NotificationHandlerContext {
58+
return &pushNotificationHandlerContext{
59+
client: client,
60+
connPool: connPool,
61+
pubSub: pubSub,
62+
conn: conn,
63+
isBlocking: isBlocking,
64+
}
65+
}
66+
67+
// GetClient returns the Redis client instance that received the notification
68+
func (h *pushNotificationHandlerContext) GetClient() interface{} {
69+
return h.client
70+
}
71+
72+
// GetConnPool returns the connection pool from which the connection was obtained
73+
func (h *pushNotificationHandlerContext) GetConnPool() interface{} {
74+
return h.connPool
75+
}
76+
77+
func (h *pushNotificationHandlerContext) GetPubSub() interface{} {
78+
return h.pubSub
79+
}
80+
81+
// GetConn returns the specific connection on which the notification was received
82+
func (h *pushNotificationHandlerContext) GetConn() *pool.Conn {
83+
return h.conn
84+
}
85+
86+
// IsBlocking returns true if the notification was received on a blocking connection
87+
func (h *pushNotificationHandlerContext) IsBlocking() bool {
88+
return h.isBlocking
89+
}

0 commit comments

Comments
 (0)