Merge pull request #2344 from monkey92t/hook

docs: add a description of the hook
This commit is contained in:
Monkey 2023-01-07 16:52:12 +08:00 committed by GitHub
commit 09fcdbe6e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 132 additions and 79 deletions

View File

@ -136,10 +136,13 @@ func (opt *ClusterOptions) init() {
// ParseClusterURL parses a URL into ClusterOptions that can be used to connect to Redis. // ParseClusterURL parses a URL into ClusterOptions that can be used to connect to Redis.
// The URL must be in the form: // The URL must be in the form:
//
// redis://<user>:<password>@<host>:<port> // redis://<user>:<password>@<host>:<port>
// or // or
// rediss://<user>:<password>@<host>:<port> // rediss://<user>:<password>@<host>:<port>
//
// To add additional addresses, specify the query parameter, "addr" one or more times. e.g: // To add additional addresses, specify the query parameter, "addr" one or more times. e.g:
//
// redis://<user>:<password>@<host>:<port>?addr=<host2>:<port2>&addr=<host3>:<port3> // redis://<user>:<password>@<host>:<port>?addr=<host2>:<port2>&addr=<host3>:<port3>
// or // or
// rediss://<user>:<password>@<host>:<port>?addr=<host2>:<port2>&addr=<host3>:<port3> // rediss://<user>:<password>@<host>:<port>?addr=<host2>:<port2>&addr=<host3>:<port3>
@ -156,7 +159,9 @@ func (opt *ClusterOptions) init() {
// URL attributes (scheme, host, userinfo, resp.), query paremeters using these // URL attributes (scheme, host, userinfo, resp.), query paremeters using these
// names will be treated as unknown parameters // names will be treated as unknown parameters
// - unknown parameter names will result in an error // - unknown parameter names will result in an error
//
// Example: // Example:
//
// redis://user:password@localhost:6789?dial_timeout=3&read_timeout=6s&addr=localhost:6790&addr=localhost:6791 // redis://user:password@localhost:6789?dial_timeout=3&read_timeout=6s&addr=localhost:6790&addr=localhost:6791
// is equivalent to: // is equivalent to:
// &ClusterOptions{ // &ClusterOptions{

View File

@ -1110,15 +1110,16 @@ func (cmd *KeyValueSliceCmd) String() string {
} }
// Many commands will respond to two formats: // Many commands will respond to two formats:
// 1) 1) "one" // 1. 1) "one"
// 2) (double) 1 // 2. (double) 1
// 2) 1) "two" // 2. 1) "two"
// 2) (double) 2 // 2. (double) 2
//
// OR: // OR:
// 1) "two" // 1. "two"
// 2) (double) 2 // 2. (double) 2
// 3) "one" // 3. "one"
// 4) (double) 1 // 4. (double) 1
func (cmd *KeyValueSliceCmd) readReply(rd *proto.Reader) error { // nolint:dupl func (cmd *KeyValueSliceCmd) readReply(rd *proto.Reader) error { // nolint:dupl
n, err := rd.ReadArrayLen() n, err := rd.ReadArrayLen()
if err != nil { if err != nil {

View File

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

View File

@ -11,6 +11,7 @@ import (
) )
// Scan parses bytes `b` to `v` with appropriate type. // Scan parses bytes `b` to `v` with appropriate type.
//
//nolint:gocyclo //nolint:gocyclo
func Scan(b []byte, v interface{}) error { func Scan(b []byte, v interface{}) error {
switch v := v.(type) { switch v := v.(type) {

View File

@ -223,9 +223,13 @@ func NewDialer(opt *Options) func(context.Context, string, string) (net.Conn, er
// Scheme is required. // Scheme is required.
// There are two connection types: by tcp socket and by unix socket. // There are two connection types: by tcp socket and by unix socket.
// Tcp connection: // Tcp connection:
//
// redis://<user>:<password>@<host>:<port>/<db_number> // redis://<user>:<password>@<host>:<port>/<db_number>
//
// Unix connection: // Unix connection:
//
// unix://<user>:<password>@</path/to/redis.sock>?db=<db_number> // unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>
//
// Most Option fields can be set using query parameters, with the following restrictions: // Most Option fields can be set using query parameters, with the following restrictions:
// - field names are mapped using snake-case conversion: to set MaxRetries, use max_retries // - field names are mapped using snake-case conversion: to set MaxRetries, use max_retries
// - only scalar type fields are supported (bool, int, time.Duration) // - only scalar type fields are supported (bool, int, time.Duration)
@ -238,7 +242,9 @@ func NewDialer(opt *Options) func(context.Context, string, string) (net.Conn, er
// URL attributes (scheme, host, userinfo, resp.), query paremeters using these // URL attributes (scheme, host, userinfo, resp.), query paremeters using these
// names will be treated as unknown parameters // names will be treated as unknown parameters
// - unknown parameter names will result in an error // - unknown parameter names will result in an error
//
// Examples: // Examples:
//
// redis://user:password@localhost:6789/3?dial_timeout=3&db=1&read_timeout=6s&max_retries=2 // redis://user:password@localhost:6789/3?dial_timeout=3&db=1&read_timeout=6s&max_retries=2
// is equivalent to: // is equivalent to:
// &Options{ // &Options{

View File

@ -29,9 +29,9 @@ func SetLogger(logger internal.Logging) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
type Hook interface { type Hook interface {
DialHook(hook DialHook) DialHook DialHook(next DialHook) DialHook
ProcessHook(hook ProcessHook) ProcessHook ProcessHook(next ProcessHook) ProcessHook
ProcessPipelineHook(hook ProcessPipelineHook) ProcessPipelineHook ProcessPipelineHook(next ProcessPipelineHook) ProcessPipelineHook
} }
type ( type (
@ -48,6 +48,43 @@ type hooks struct {
processTxPipelineHook ProcessPipelineHook processTxPipelineHook ProcessPipelineHook
} }
// AddHook is to add a hook to the queue.
// Hook is a function executed during network connection, command execution, and pipeline,
// it is a first-in-last-out stack queue (FILO).
// The first to be added to the queue is the execution function of the redis command (the last to be executed).
// You need to execute the next hook in each hook, unless you want to terminate the execution of the command.
// For example, you added hook-1, hook-2:
//
// client.AddHook(hook-1, hook-2)
//
// hook-1:
//
// func (Hook1) ProcessHook(next redis.ProcessHook) redis.ProcessHook {
// return func(ctx context.Context, cmd Cmder) error {
// print("hook-1 start")
// next(ctx, cmd)
// print("hook-1 end")
// return nil
// }
// }
//
// hook-2:
//
// func (Hook2) ProcessHook(next redis.ProcessHook) redis.ProcessHook {
// return func(ctx context.Context, cmd redis.Cmder) error {
// print("hook-2 start")
// next(ctx, cmd)
// print("hook-2 end")
// return nil
// }
// }
//
// The execution sequence is:
//
// hook-2 start -> hook-1 start -> exec redis cmd -> hook-1 end -> hook-2 end
//
// Please note: "next(ctx, cmd)" is very important, it will call the next hook,
// if "next(ctx, cmd)" is not executed in hook-1, the redis command will not be executed.
func (hs *hooks) AddHook(hook Hook) { func (hs *hooks) AddHook(hook Hook) {
hs.slice = append(hs.slice, hook) hs.slice = append(hs.slice, hook)
hs.dialHook = hook.DialHook(hs.dialHook) hs.dialHook = hook.DialHook(hs.dialHook)
@ -575,7 +612,7 @@ func (c *Client) Conn() *Conn {
return newConn(c.opt, pool.NewStickyConnPool(c.connPool)) return newConn(c.opt, pool.NewStickyConnPool(c.connPool))
} }
// Do creates a Cmd from the args and processes the cmd. // Do create a Cmd from the args and processes the cmd.
func (c *Client) Do(ctx context.Context, args ...interface{}) *Cmd { func (c *Client) Do(ctx context.Context, args ...interface{}) *Cmd {
cmd := NewCmd(ctx, args...) cmd := NewCmd(ctx, args...)
_ = c.Process(ctx, cmd) _ = c.Process(ctx, cmd)