Skip to content

Commit 09fcdbe

Browse files
authored
Merge pull request #2344 from monkey92t/hook
docs: add a description of the hook
2 parents af89314 + d42dd10 commit 09fcdbe

File tree

6 files changed

+132
-79
lines changed

6 files changed

+132
-79
lines changed

cluster.go

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -136,34 +136,39 @@ func (opt *ClusterOptions) init() {
136136

137137
// ParseClusterURL parses a URL into ClusterOptions that can be used to connect to Redis.
138138
// The URL must be in the form:
139-
// redis://<user>:<password>@<host>:<port>
140-
// or
141-
// rediss://<user>:<password>@<host>:<port>
139+
//
140+
// redis://<user>:<password>@<host>:<port>
141+
// or
142+
// rediss://<user>:<password>@<host>:<port>
143+
//
142144
// To add additional addresses, specify the query parameter, "addr" one or more times. e.g:
143-
// redis://<user>:<password>@<host>:<port>?addr=<host2>:<port2>&addr=<host3>:<port3>
144-
// or
145-
// rediss://<user>:<password>@<host>:<port>?addr=<host2>:<port2>&addr=<host3>:<port3>
145+
//
146+
// redis://<user>:<password>@<host>:<port>?addr=<host2>:<port2>&addr=<host3>:<port3>
147+
// or
148+
// rediss://<user>:<password>@<host>:<port>?addr=<host2>:<port2>&addr=<host3>:<port3>
146149
//
147150
// Most Option fields can be set using query parameters, with the following restrictions:
148-
// - field names are mapped using snake-case conversion: to set MaxRetries, use max_retries
149-
// - only scalar type fields are supported (bool, int, time.Duration)
150-
// - for time.Duration fields, values must be a valid input for time.ParseDuration();
151-
// additionally a plain integer as value (i.e. without unit) is intepreted as seconds
152-
// - to disable a duration field, use value less than or equal to 0; to use the default
153-
// value, leave the value blank or remove the parameter
154-
// - only the last value is interpreted if a parameter is given multiple times
155-
// - fields "network", "addr", "username" and "password" can only be set using other
156-
// URL attributes (scheme, host, userinfo, resp.), query paremeters using these
157-
// names will be treated as unknown parameters
158-
// - unknown parameter names will result in an error
151+
// - field names are mapped using snake-case conversion: to set MaxRetries, use max_retries
152+
// - only scalar type fields are supported (bool, int, time.Duration)
153+
// - for time.Duration fields, values must be a valid input for time.ParseDuration();
154+
// additionally a plain integer as value (i.e. without unit) is intepreted as seconds
155+
// - to disable a duration field, use value less than or equal to 0; to use the default
156+
// value, leave the value blank or remove the parameter
157+
// - only the last value is interpreted if a parameter is given multiple times
158+
// - fields "network", "addr", "username" and "password" can only be set using other
159+
// URL attributes (scheme, host, userinfo, resp.), query paremeters using these
160+
// names will be treated as unknown parameters
161+
// - unknown parameter names will result in an error
162+
//
159163
// Example:
160-
// redis://user:password@localhost:6789?dial_timeout=3&read_timeout=6s&addr=localhost:6790&addr=localhost:6791
161-
// is equivalent to:
162-
// &ClusterOptions{
163-
// Addr: ["localhost:6789", "localhost:6790", "localhost:6791"]
164-
// DialTimeout: 3 * time.Second, // no time unit = seconds
165-
// ReadTimeout: 6 * time.Second,
166-
// }
164+
//
165+
// redis://user:password@localhost:6789?dial_timeout=3&read_timeout=6s&addr=localhost:6790&addr=localhost:6791
166+
// is equivalent to:
167+
// &ClusterOptions{
168+
// Addr: ["localhost:6789", "localhost:6790", "localhost:6791"]
169+
// DialTimeout: 3 * time.Second, // no time unit = seconds
170+
// ReadTimeout: 6 * time.Second,
171+
// }
167172
func ParseClusterURL(redisURL string) (*ClusterOptions, error) {
168173
o := &ClusterOptions{}
169174

command.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,15 +1110,16 @@ func (cmd *KeyValueSliceCmd) String() string {
11101110
}
11111111

11121112
// Many commands will respond to two formats:
1113-
// 1) 1) "one"
1114-
// 2) (double) 1
1115-
// 2) 1) "two"
1116-
// 2) (double) 2
1113+
// 1. 1) "one"
1114+
// 2. (double) 1
1115+
// 2. 1) "two"
1116+
// 2. (double) 2
1117+
//
11171118
// OR:
1118-
// 1) "two"
1119-
// 2) (double) 2
1120-
// 3) "one"
1121-
// 4) (double) 1
1119+
// 1. "two"
1120+
// 2. (double) 2
1121+
// 3. "one"
1122+
// 4. (double) 1
11221123
func (cmd *KeyValueSliceCmd) readReply(rd *proto.Reader) error { // nolint:dupl
11231124
n, err := rd.ReadArrayLen()
11241125
if err != nil {

internal/once.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ type Once struct {
3232

3333
// Do calls the function f if and only if Do has not been invoked
3434
// without error for this instance of Once. In other words, given
35-
// var once Once
35+
//
36+
// var once Once
37+
//
3638
// if once.Do(f) is called multiple times, only the first call will
3739
// invoke f, even if f has a different value in each invocation unless
3840
// f returns an error. A new instance of Once is required for each
@@ -41,7 +43,8 @@ type Once struct {
4143
// Do is intended for initialization that must be run exactly once. Since f
4244
// is niladic, it may be necessary to use a function literal to capture the
4345
// arguments to a function to be invoked by Do:
44-
// err := config.once.Do(func() error { return config.init(filename) })
46+
//
47+
// err := config.once.Do(func() error { return config.init(filename) })
4548
func (o *Once) Do(f func() error) error {
4649
if atomic.LoadUint32(&o.done) == 1 {
4750
return nil

internal/proto/scan.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
)
1212

1313
// Scan parses bytes `b` to `v` with appropriate type.
14+
//
1415
//nolint:gocyclo
1516
func Scan(b []byte, v interface{}) error {
1617
switch v := v.(type) {

options.go

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -223,32 +223,38 @@ func NewDialer(opt *Options) func(context.Context, string, string) (net.Conn, er
223223
// Scheme is required.
224224
// There are two connection types: by tcp socket and by unix socket.
225225
// Tcp connection:
226-
// redis://<user>:<password>@<host>:<port>/<db_number>
226+
//
227+
// redis://<user>:<password>@<host>:<port>/<db_number>
228+
//
227229
// Unix connection:
228-
// unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>
230+
//
231+
// unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>
232+
//
229233
// Most Option fields can be set using query parameters, with the following restrictions:
230-
// - field names are mapped using snake-case conversion: to set MaxRetries, use max_retries
231-
// - only scalar type fields are supported (bool, int, time.Duration)
232-
// - for time.Duration fields, values must be a valid input for time.ParseDuration();
233-
// additionally a plain integer as value (i.e. without unit) is intepreted as seconds
234-
// - to disable a duration field, use value less than or equal to 0; to use the default
235-
// value, leave the value blank or remove the parameter
236-
// - only the last value is interpreted if a parameter is given multiple times
237-
// - fields "network", "addr", "username" and "password" can only be set using other
238-
// URL attributes (scheme, host, userinfo, resp.), query paremeters using these
239-
// names will be treated as unknown parameters
240-
// - unknown parameter names will result in an error
234+
// - field names are mapped using snake-case conversion: to set MaxRetries, use max_retries
235+
// - only scalar type fields are supported (bool, int, time.Duration)
236+
// - for time.Duration fields, values must be a valid input for time.ParseDuration();
237+
// additionally a plain integer as value (i.e. without unit) is intepreted as seconds
238+
// - to disable a duration field, use value less than or equal to 0; to use the default
239+
// value, leave the value blank or remove the parameter
240+
// - only the last value is interpreted if a parameter is given multiple times
241+
// - fields "network", "addr", "username" and "password" can only be set using other
242+
// URL attributes (scheme, host, userinfo, resp.), query paremeters using these
243+
// names will be treated as unknown parameters
244+
// - unknown parameter names will result in an error
245+
//
241246
// Examples:
242-
// redis://user:password@localhost:6789/3?dial_timeout=3&db=1&read_timeout=6s&max_retries=2
243-
// is equivalent to:
244-
// &Options{
245-
// Network: "tcp",
246-
// Addr: "localhost:6789",
247-
// DB: 1, // path "/3" was overridden by "&db=1"
248-
// DialTimeout: 3 * time.Second, // no time unit = seconds
249-
// ReadTimeout: 6 * time.Second,
250-
// MaxRetries: 2,
251-
// }
247+
//
248+
// redis://user:password@localhost:6789/3?dial_timeout=3&db=1&read_timeout=6s&max_retries=2
249+
// is equivalent to:
250+
// &Options{
251+
// Network: "tcp",
252+
// Addr: "localhost:6789",
253+
// DB: 1, // path "/3" was overridden by "&db=1"
254+
// DialTimeout: 3 * time.Second, // no time unit = seconds
255+
// ReadTimeout: 6 * time.Second,
256+
// MaxRetries: 2,
257+
// }
252258
func ParseURL(redisURL string) (*Options, error) {
253259
u, err := url.Parse(redisURL)
254260
if err != nil {

redis.go

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ func SetLogger(logger internal.Logging) {
2929
//------------------------------------------------------------------------------
3030

3131
type Hook interface {
32-
DialHook(hook DialHook) DialHook
33-
ProcessHook(hook ProcessHook) ProcessHook
34-
ProcessPipelineHook(hook ProcessPipelineHook) ProcessPipelineHook
32+
DialHook(next DialHook) DialHook
33+
ProcessHook(next ProcessHook) ProcessHook
34+
ProcessPipelineHook(next ProcessPipelineHook) ProcessPipelineHook
3535
}
3636

3737
type (
@@ -48,6 +48,43 @@ type hooks struct {
4848
processTxPipelineHook ProcessPipelineHook
4949
}
5050

51+
// AddHook is to add a hook to the queue.
52+
// Hook is a function executed during network connection, command execution, and pipeline,
53+
// it is a first-in-last-out stack queue (FILO).
54+
// The first to be added to the queue is the execution function of the redis command (the last to be executed).
55+
// You need to execute the next hook in each hook, unless you want to terminate the execution of the command.
56+
// For example, you added hook-1, hook-2:
57+
//
58+
// client.AddHook(hook-1, hook-2)
59+
//
60+
// hook-1:
61+
//
62+
// func (Hook1) ProcessHook(next redis.ProcessHook) redis.ProcessHook {
63+
// return func(ctx context.Context, cmd Cmder) error {
64+
// print("hook-1 start")
65+
// next(ctx, cmd)
66+
// print("hook-1 end")
67+
// return nil
68+
// }
69+
// }
70+
//
71+
// hook-2:
72+
//
73+
// func (Hook2) ProcessHook(next redis.ProcessHook) redis.ProcessHook {
74+
// return func(ctx context.Context, cmd redis.Cmder) error {
75+
// print("hook-2 start")
76+
// next(ctx, cmd)
77+
// print("hook-2 end")
78+
// return nil
79+
// }
80+
// }
81+
//
82+
// The execution sequence is:
83+
//
84+
// hook-2 start -> hook-1 start -> exec redis cmd -> hook-1 end -> hook-2 end
85+
//
86+
// Please note: "next(ctx, cmd)" is very important, it will call the next hook,
87+
// if "next(ctx, cmd)" is not executed in hook-1, the redis command will not be executed.
5188
func (hs *hooks) AddHook(hook Hook) {
5289
hs.slice = append(hs.slice, hook)
5390
hs.dialHook = hook.DialHook(hs.dialHook)
@@ -575,7 +612,7 @@ func (c *Client) Conn() *Conn {
575612
return newConn(c.opt, pool.NewStickyConnPool(c.connPool))
576613
}
577614

578-
// Do creates a Cmd from the args and processes the cmd.
615+
// Do create a Cmd from the args and processes the cmd.
579616
func (c *Client) Do(ctx context.Context, args ...interface{}) *Cmd {
580617
cmd := NewCmd(ctx, args...)
581618
_ = c.Process(ctx, cmd)
@@ -648,26 +685,26 @@ func (c *Client) pubSub() *PubSub {
648685
// subscription may not be active immediately. To force the connection to wait,
649686
// you may call the Receive() method on the returned *PubSub like so:
650687
//
651-
// sub := client.Subscribe(queryResp)
652-
// iface, err := sub.Receive()
653-
// if err != nil {
654-
// // handle error
655-
// }
688+
// sub := client.Subscribe(queryResp)
689+
// iface, err := sub.Receive()
690+
// if err != nil {
691+
// // handle error
692+
// }
656693
//
657-
// // Should be *Subscription, but others are possible if other actions have been
658-
// // taken on sub since it was created.
659-
// switch iface.(type) {
660-
// case *Subscription:
661-
// // subscribe succeeded
662-
// case *Message:
663-
// // received first message
664-
// case *Pong:
665-
// // pong received
666-
// default:
667-
// // handle error
668-
// }
694+
// // Should be *Subscription, but others are possible if other actions have been
695+
// // taken on sub since it was created.
696+
// switch iface.(type) {
697+
// case *Subscription:
698+
// // subscribe succeeded
699+
// case *Message:
700+
// // received first message
701+
// case *Pong:
702+
// // pong received
703+
// default:
704+
// // handle error
705+
// }
669706
//
670-
// ch := sub.Channel()
707+
// ch := sub.Channel()
671708
func (c *Client) Subscribe(ctx context.Context, channels ...string) *PubSub {
672709
pubsub := c.pubSub()
673710
if len(channels) > 0 {

0 commit comments

Comments
 (0)