|
5 | 5 | "fmt"
|
6 | 6 | "net"
|
7 | 7 | "strconv"
|
| 8 | + "strings" |
8 | 9 | "time"
|
9 | 10 |
|
10 | 11 | "github.com/redis/go-redis/v9/internal"
|
@@ -3992,3 +3993,269 @@ func (cmd *FunctionListCmd) readFunctions(rd *proto.Reader) ([]Function, error)
|
3992 | 3993 | }
|
3993 | 3994 | return functions, nil
|
3994 | 3995 | }
|
| 3996 | + |
| 3997 | +// -------------------------------------------------------------------------------------------------- |
| 3998 | + |
| 3999 | +// ClientFlags is redis-server client flags, copy from redis/src/server.h (redis 7.0) |
| 4000 | +type ClientFlags uint64 |
| 4001 | + |
| 4002 | +const ( |
| 4003 | + ClientSlave ClientFlags = 1 << 0 /* This client is a replica */ |
| 4004 | + ClientMaster ClientFlags = 1 << 1 /* This client is a master */ |
| 4005 | + ClientMonitor ClientFlags = 1 << 2 /* This client is a slave monitor, see MONITOR */ |
| 4006 | + ClientMulti ClientFlags = 1 << 3 /* This client is in a MULTI context */ |
| 4007 | + ClientBlocked ClientFlags = 1 << 4 /* The client is waiting in a blocking operation */ |
| 4008 | + ClientDirtyCAS ClientFlags = 1 << 5 /* Watched keys modified. EXEC will fail. */ |
| 4009 | + ClientCloseAfterReply ClientFlags = 1 << 6 /* Close after writing entire reply. */ |
| 4010 | + ClientUnBlocked ClientFlags = 1 << 7 /* This client was unblocked and is stored in server.unblocked_clients */ |
| 4011 | + ClientScript ClientFlags = 1 << 8 /* This is a non-connected client used by Lua */ |
| 4012 | + ClientAsking ClientFlags = 1 << 9 /* Client issued the ASKING command */ |
| 4013 | + ClientCloseASAP ClientFlags = 1 << 10 /* Close this client ASAP */ |
| 4014 | + ClientUnixSocket ClientFlags = 1 << 11 /* Client connected via Unix domain socket */ |
| 4015 | + ClientDirtyExec ClientFlags = 1 << 12 /* EXEC will fail for errors while queueing */ |
| 4016 | + ClientMasterForceReply ClientFlags = 1 << 13 /* Queue replies even if is master */ |
| 4017 | + ClientForceAOF ClientFlags = 1 << 14 /* Force AOF propagation of current cmd. */ |
| 4018 | + ClientForceRepl ClientFlags = 1 << 15 /* Force replication of current cmd. */ |
| 4019 | + ClientPrePSync ClientFlags = 1 << 16 /* Instance don't understand PSYNC. */ |
| 4020 | + ClientReadOnly ClientFlags = 1 << 17 /* Cluster client is in read-only state. */ |
| 4021 | + ClientPubSub ClientFlags = 1 << 18 /* Client is in Pub/Sub mode. */ |
| 4022 | + ClientPreventAOFProp ClientFlags = 1 << 19 /* Don't propagate to AOF. */ |
| 4023 | + ClientPreventReplProp ClientFlags = 1 << 20 /* Don't propagate to slaves. */ |
| 4024 | + ClientPreventProp ClientFlags = ClientPreventAOFProp | ClientPreventReplProp |
| 4025 | + ClientPendingWrite ClientFlags = 1 << 21 /* Client has output to send but a-write handler is yet not installed. */ |
| 4026 | + ClientReplyOff ClientFlags = 1 << 22 /* Don't send replies to client. */ |
| 4027 | + ClientReplySkipNext ClientFlags = 1 << 23 /* Set ClientREPLY_SKIP for next cmd */ |
| 4028 | + ClientReplySkip ClientFlags = 1 << 24 /* Don't send just this reply. */ |
| 4029 | + ClientLuaDebug ClientFlags = 1 << 25 /* Run EVAL in debug mode. */ |
| 4030 | + ClientLuaDebugSync ClientFlags = 1 << 26 /* EVAL debugging without fork() */ |
| 4031 | + ClientModule ClientFlags = 1 << 27 /* Non connected client used by some module. */ |
| 4032 | + ClientProtected ClientFlags = 1 << 28 /* Client should not be freed for now. */ |
| 4033 | + ClientExecutingCommand ClientFlags = 1 << 29 /* Indicates that the client is currently in the process of handling |
| 4034 | + a command. usually this will be marked only during call() |
| 4035 | + however, blocked clients might have this flag kept until they |
| 4036 | + will try to reprocess the command. */ |
| 4037 | + ClientPendingCommand ClientFlags = 1 << 30 /* Indicates the client has a fully * parsed command ready for execution. */ |
| 4038 | + ClientTracking ClientFlags = 1 << 31 /* Client enabled keys tracking in order to perform client side caching. */ |
| 4039 | + ClientTrackingBrokenRedir ClientFlags = 1 << 32 /* Target client is invalid. */ |
| 4040 | + ClientTrackingBCAST ClientFlags = 1 << 33 /* Tracking in BCAST mode. */ |
| 4041 | + ClientTrackingOptIn ClientFlags = 1 << 34 /* Tracking in opt-in mode. */ |
| 4042 | + ClientTrackingOptOut ClientFlags = 1 << 35 /* Tracking in opt-out mode. */ |
| 4043 | + ClientTrackingCaching ClientFlags = 1 << 36 /* CACHING yes/no was given, depending on optin/optout mode. */ |
| 4044 | + ClientTrackingNoLoop ClientFlags = 1 << 37 /* Don't send invalidation messages about writes performed by myself.*/ |
| 4045 | + ClientInTimeoutTable ClientFlags = 1 << 38 /* This client is in the timeout table. */ |
| 4046 | + ClientProtocolError ClientFlags = 1 << 39 /* Protocol error chatting with it. */ |
| 4047 | + ClientCloseAfterCommand ClientFlags = 1 << 40 /* Close after executing commands * and writing entire reply. */ |
| 4048 | + ClientDenyBlocking ClientFlags = 1 << 41 /* Indicate that the client should not be blocked. currently, turned on inside MULTI, Lua, RM_Call, and AOF client */ |
| 4049 | + ClientReplRDBOnly ClientFlags = 1 << 42 /* This client is a replica that only wants RDB without replication buffer. */ |
| 4050 | + ClientNoEvict ClientFlags = 1 << 43 /* This client is protected against client memory eviction. */ |
| 4051 | + ClientAllowOOM ClientFlags = 1 << 44 /* Client used by RM_Call is allowed to fully execute scripts even when in OOM */ |
| 4052 | + ClientNoTouch ClientFlags = 1 << 45 /* This client will not touch LFU/LRU stats. */ |
| 4053 | + ClientPushing ClientFlags = 1 << 46 /* This client is pushing notifications. */ |
| 4054 | +) |
| 4055 | + |
| 4056 | +// ClientInfo is redis-server ClientInfo, not go-redis *Client |
| 4057 | +type ClientInfo struct { |
| 4058 | + ID int64 // redis version 2.8.12, a unique 64-bit client ID |
| 4059 | + Addr string // address/port of the client |
| 4060 | + LAddr string // address/port of local address client connected to (bind address) |
| 4061 | + FD int64 // file descriptor corresponding to the socket |
| 4062 | + Name string // the name set by the client with CLIENT SETNAME |
| 4063 | + Age time.Duration // total duration of the connection in seconds |
| 4064 | + Idle time.Duration // idle time of the connection in seconds |
| 4065 | + Flags ClientFlags // client flags (see below) |
| 4066 | + DB int // current database ID |
| 4067 | + Sub int // number of channel subscriptions |
| 4068 | + PSub int // number of pattern matching subscriptions |
| 4069 | + SSub int // redis version 7.0.3, number of shard channel subscriptions |
| 4070 | + Multi int // number of commands in a MULTI/EXEC context |
| 4071 | + QueryBuf int // qbuf, query buffer length (0 means no query pending) |
| 4072 | + QueryBufFree int // qbuf-free, free space of the query buffer (0 means the buffer is full) |
| 4073 | + ArgvMem int // incomplete arguments for the next command (already extracted from query buffer) |
| 4074 | + MultiMem int // redis version 7.0, memory is used up by buffered multi commands |
| 4075 | + BufferSize int // rbs, usable size of buffer |
| 4076 | + BufferPeak int // rbp, peak used size of buffer in last 5 sec interval |
| 4077 | + OutputBufferLength int // obl, output buffer length |
| 4078 | + OutputListLength int // oll, output list length (replies are queued in this list when the buffer is full) |
| 4079 | + OutputMemory int // omem, output buffer memory usage |
| 4080 | + TotalMemory int // tot-mem, total memory consumed by this client in its various buffers |
| 4081 | + Events string // file descriptor events (see below) |
| 4082 | + LastCmd string // cmd, last command played |
| 4083 | + User string // the authenticated username of the client |
| 4084 | + Redir int64 // client id of current client tracking redirection |
| 4085 | + Resp int // redis version 7.0, client RESP protocol version |
| 4086 | +} |
| 4087 | + |
| 4088 | +type ClientInfoCmd struct { |
| 4089 | + baseCmd |
| 4090 | + |
| 4091 | + val *ClientInfo |
| 4092 | +} |
| 4093 | + |
| 4094 | +var _ Cmder = (*ClientInfoCmd)(nil) |
| 4095 | + |
| 4096 | +func NewClientInfoCmd(ctx context.Context, args ...interface{}) *ClientInfoCmd { |
| 4097 | + return &ClientInfoCmd{ |
| 4098 | + baseCmd: baseCmd{ |
| 4099 | + ctx: ctx, |
| 4100 | + args: args, |
| 4101 | + }, |
| 4102 | + } |
| 4103 | +} |
| 4104 | + |
| 4105 | +func (cmd *ClientInfoCmd) SetVal(val *ClientInfo) { |
| 4106 | + cmd.val = val |
| 4107 | +} |
| 4108 | + |
| 4109 | +func (cmd *ClientInfoCmd) String() string { |
| 4110 | + return cmdString(cmd, cmd.val) |
| 4111 | +} |
| 4112 | + |
| 4113 | +func (cmd *ClientInfoCmd) Val() *ClientInfo { |
| 4114 | + return cmd.val |
| 4115 | +} |
| 4116 | + |
| 4117 | +func (cmd *ClientInfoCmd) Result() (*ClientInfo, error) { |
| 4118 | + return cmd.val, cmd.err |
| 4119 | +} |
| 4120 | + |
| 4121 | +func (cmd *ClientInfoCmd) readReply(rd *proto.Reader) (err error) { |
| 4122 | + txt, err := rd.ReadString() |
| 4123 | + if err != nil { |
| 4124 | + return err |
| 4125 | + } |
| 4126 | + |
| 4127 | + // sds o = catClientInfoString(sdsempty(), c); |
| 4128 | + // o = sdscatlen(o,"\n",1); |
| 4129 | + // addReplyVerbatim(c,o,sdslen(o),"txt"); |
| 4130 | + // sdsfree(o); |
| 4131 | + cmd.val, err = parseClientInfo(strings.TrimSpace(txt)) |
| 4132 | + return err |
| 4133 | +} |
| 4134 | + |
| 4135 | +// fmt.Sscanf() cannot handle null values |
| 4136 | +func parseClientInfo(txt string) (info *ClientInfo, err error) { |
| 4137 | + info = &ClientInfo{} |
| 4138 | + for _, s := range strings.Split(txt, " ") { |
| 4139 | + kv := strings.Split(s, "=") |
| 4140 | + if len(kv) != 2 { |
| 4141 | + return nil, fmt.Errorf("redis: unexpected client info data (%s)", s) |
| 4142 | + } |
| 4143 | + key, val := kv[0], kv[1] |
| 4144 | + |
| 4145 | + switch key { |
| 4146 | + case "id": |
| 4147 | + info.ID, err = strconv.ParseInt(val, 10, 64) |
| 4148 | + case "addr": |
| 4149 | + info.Addr = val |
| 4150 | + case "laddr": |
| 4151 | + info.LAddr = val |
| 4152 | + case "fd": |
| 4153 | + info.FD, err = strconv.ParseInt(val, 10, 64) |
| 4154 | + case "name": |
| 4155 | + info.Name = val |
| 4156 | + case "age": |
| 4157 | + var age int |
| 4158 | + if age, err = strconv.Atoi(val); err == nil { |
| 4159 | + info.Age = time.Duration(age) * time.Second |
| 4160 | + } |
| 4161 | + case "idle": |
| 4162 | + var idle int |
| 4163 | + if idle, err = strconv.Atoi(val); err == nil { |
| 4164 | + info.Idle = time.Duration(idle) * time.Second |
| 4165 | + } |
| 4166 | + case "flags": |
| 4167 | + if val == "N" { |
| 4168 | + break |
| 4169 | + } |
| 4170 | + |
| 4171 | + for i := 0; i < len(val); i++ { |
| 4172 | + switch val[i] { |
| 4173 | + case 'S': |
| 4174 | + info.Flags |= ClientSlave |
| 4175 | + case 'O': |
| 4176 | + info.Flags |= ClientSlave | ClientMonitor |
| 4177 | + case 'M': |
| 4178 | + info.Flags |= ClientMaster |
| 4179 | + case 'P': |
| 4180 | + info.Flags |= ClientPubSub |
| 4181 | + case 'x': |
| 4182 | + info.Flags |= ClientMulti |
| 4183 | + case 'b': |
| 4184 | + info.Flags |= ClientBlocked |
| 4185 | + case 't': |
| 4186 | + info.Flags |= ClientTracking |
| 4187 | + case 'R': |
| 4188 | + info.Flags |= ClientTrackingBrokenRedir |
| 4189 | + case 'B': |
| 4190 | + info.Flags |= ClientTrackingBCAST |
| 4191 | + case 'd': |
| 4192 | + info.Flags |= ClientDirtyCAS |
| 4193 | + case 'c': |
| 4194 | + info.Flags |= ClientCloseAfterCommand |
| 4195 | + case 'u': |
| 4196 | + info.Flags |= ClientUnBlocked |
| 4197 | + case 'A': |
| 4198 | + info.Flags |= ClientCloseASAP |
| 4199 | + case 'U': |
| 4200 | + info.Flags |= ClientUnixSocket |
| 4201 | + case 'r': |
| 4202 | + info.Flags |= ClientReadOnly |
| 4203 | + case 'e': |
| 4204 | + info.Flags |= ClientNoEvict |
| 4205 | + case 'T': |
| 4206 | + info.Flags |= ClientNoTouch |
| 4207 | + default: |
| 4208 | + return nil, fmt.Errorf("redis: unexpected client info flags(%s)", string(val[i])) |
| 4209 | + } |
| 4210 | + } |
| 4211 | + case "db": |
| 4212 | + info.DB, err = strconv.Atoi(val) |
| 4213 | + case "sub": |
| 4214 | + info.Sub, err = strconv.Atoi(val) |
| 4215 | + case "psub": |
| 4216 | + info.PSub, err = strconv.Atoi(val) |
| 4217 | + case "ssub": |
| 4218 | + info.SSub, err = strconv.Atoi(val) |
| 4219 | + case "multi": |
| 4220 | + info.Multi, err = strconv.Atoi(val) |
| 4221 | + case "qbuf": |
| 4222 | + info.QueryBuf, err = strconv.Atoi(val) |
| 4223 | + case "qbuf-free": |
| 4224 | + info.QueryBufFree, err = strconv.Atoi(val) |
| 4225 | + case "argv-mem": |
| 4226 | + info.ArgvMem, err = strconv.Atoi(val) |
| 4227 | + case "multi-mem": |
| 4228 | + info.MultiMem, err = strconv.Atoi(val) |
| 4229 | + case "rbs": |
| 4230 | + info.BufferSize, err = strconv.Atoi(val) |
| 4231 | + case "rbp": |
| 4232 | + info.BufferPeak, err = strconv.Atoi(val) |
| 4233 | + case "obl": |
| 4234 | + info.OutputBufferLength, err = strconv.Atoi(val) |
| 4235 | + case "oll": |
| 4236 | + info.OutputListLength, err = strconv.Atoi(val) |
| 4237 | + case "omem": |
| 4238 | + info.OutputMemory, err = strconv.Atoi(val) |
| 4239 | + case "tot-mem": |
| 4240 | + info.TotalMemory, err = strconv.Atoi(val) |
| 4241 | + case "events": |
| 4242 | + info.Events = val |
| 4243 | + case "cmd": |
| 4244 | + info.LastCmd = val |
| 4245 | + case "user": |
| 4246 | + info.User = val |
| 4247 | + case "redir": |
| 4248 | + info.Redir, err = strconv.ParseInt(val, 10, 64) |
| 4249 | + case "resp": |
| 4250 | + info.Resp, err = strconv.Atoi(val) |
| 4251 | + default: |
| 4252 | + return nil, fmt.Errorf("redis: unexpected client info key(%s)", key) |
| 4253 | + } |
| 4254 | + |
| 4255 | + if err != nil { |
| 4256 | + return nil, err |
| 4257 | + } |
| 4258 | + } |
| 4259 | + |
| 4260 | + return info, nil |
| 4261 | +} |
0 commit comments