|
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"
|
@@ -4784,3 +4785,275 @@ func (cmd *RankWithScoreCmd) readReply(rd *proto.Reader) error {
|
4784 | 4785 |
|
4785 | 4786 | return nil
|
4786 | 4787 | }
|
| 4788 | + |
| 4789 | +// -------------------------------------------------------------------------------------------------- |
| 4790 | + |
| 4791 | +// ClientFlags is redis-server client flags, copy from redis/src/server.h (redis 7.0) |
| 4792 | +type ClientFlags uint64 |
| 4793 | + |
| 4794 | +const ( |
| 4795 | + ClientSlave ClientFlags = 1 << 0 /* This client is a replica */ |
| 4796 | + ClientMaster ClientFlags = 1 << 1 /* This client is a master */ |
| 4797 | + ClientMonitor ClientFlags = 1 << 2 /* This client is a slave monitor, see MONITOR */ |
| 4798 | + ClientMulti ClientFlags = 1 << 3 /* This client is in a MULTI context */ |
| 4799 | + ClientBlocked ClientFlags = 1 << 4 /* The client is waiting in a blocking operation */ |
| 4800 | + ClientDirtyCAS ClientFlags = 1 << 5 /* Watched keys modified. EXEC will fail. */ |
| 4801 | + ClientCloseAfterReply ClientFlags = 1 << 6 /* Close after writing entire reply. */ |
| 4802 | + ClientUnBlocked ClientFlags = 1 << 7 /* This client was unblocked and is stored in server.unblocked_clients */ |
| 4803 | + ClientScript ClientFlags = 1 << 8 /* This is a non-connected client used by Lua */ |
| 4804 | + ClientAsking ClientFlags = 1 << 9 /* Client issued the ASKING command */ |
| 4805 | + ClientCloseASAP ClientFlags = 1 << 10 /* Close this client ASAP */ |
| 4806 | + ClientUnixSocket ClientFlags = 1 << 11 /* Client connected via Unix domain socket */ |
| 4807 | + ClientDirtyExec ClientFlags = 1 << 12 /* EXEC will fail for errors while queueing */ |
| 4808 | + ClientMasterForceReply ClientFlags = 1 << 13 /* Queue replies even if is master */ |
| 4809 | + ClientForceAOF ClientFlags = 1 << 14 /* Force AOF propagation of current cmd. */ |
| 4810 | + ClientForceRepl ClientFlags = 1 << 15 /* Force replication of current cmd. */ |
| 4811 | + ClientPrePSync ClientFlags = 1 << 16 /* Instance don't understand PSYNC. */ |
| 4812 | + ClientReadOnly ClientFlags = 1 << 17 /* Cluster client is in read-only state. */ |
| 4813 | + ClientPubSub ClientFlags = 1 << 18 /* Client is in Pub/Sub mode. */ |
| 4814 | + ClientPreventAOFProp ClientFlags = 1 << 19 /* Don't propagate to AOF. */ |
| 4815 | + ClientPreventReplProp ClientFlags = 1 << 20 /* Don't propagate to slaves. */ |
| 4816 | + ClientPreventProp ClientFlags = ClientPreventAOFProp | ClientPreventReplProp |
| 4817 | + ClientPendingWrite ClientFlags = 1 << 21 /* Client has output to send but a-write handler is yet not installed. */ |
| 4818 | + ClientReplyOff ClientFlags = 1 << 22 /* Don't send replies to client. */ |
| 4819 | + ClientReplySkipNext ClientFlags = 1 << 23 /* Set ClientREPLY_SKIP for next cmd */ |
| 4820 | + ClientReplySkip ClientFlags = 1 << 24 /* Don't send just this reply. */ |
| 4821 | + ClientLuaDebug ClientFlags = 1 << 25 /* Run EVAL in debug mode. */ |
| 4822 | + ClientLuaDebugSync ClientFlags = 1 << 26 /* EVAL debugging without fork() */ |
| 4823 | + ClientModule ClientFlags = 1 << 27 /* Non connected client used by some module. */ |
| 4824 | + ClientProtected ClientFlags = 1 << 28 /* Client should not be freed for now. */ |
| 4825 | + ClientExecutingCommand ClientFlags = 1 << 29 /* Indicates that the client is currently in the process of handling |
| 4826 | + a command. usually this will be marked only during call() |
| 4827 | + however, blocked clients might have this flag kept until they |
| 4828 | + will try to reprocess the command. */ |
| 4829 | + ClientPendingCommand ClientFlags = 1 << 30 /* Indicates the client has a fully * parsed command ready for execution. */ |
| 4830 | + ClientTracking ClientFlags = 1 << 31 /* Client enabled keys tracking in order to perform client side caching. */ |
| 4831 | + ClientTrackingBrokenRedir ClientFlags = 1 << 32 /* Target client is invalid. */ |
| 4832 | + ClientTrackingBCAST ClientFlags = 1 << 33 /* Tracking in BCAST mode. */ |
| 4833 | + ClientTrackingOptIn ClientFlags = 1 << 34 /* Tracking in opt-in mode. */ |
| 4834 | + ClientTrackingOptOut ClientFlags = 1 << 35 /* Tracking in opt-out mode. */ |
| 4835 | + ClientTrackingCaching ClientFlags = 1 << 36 /* CACHING yes/no was given, depending on optin/optout mode. */ |
| 4836 | + ClientTrackingNoLoop ClientFlags = 1 << 37 /* Don't send invalidation messages about writes performed by myself.*/ |
| 4837 | + ClientInTimeoutTable ClientFlags = 1 << 38 /* This client is in the timeout table. */ |
| 4838 | + ClientProtocolError ClientFlags = 1 << 39 /* Protocol error chatting with it. */ |
| 4839 | + ClientCloseAfterCommand ClientFlags = 1 << 40 /* Close after executing commands * and writing entire reply. */ |
| 4840 | + ClientDenyBlocking ClientFlags = 1 << 41 /* Indicate that the client should not be blocked. currently, turned on inside MULTI, Lua, RM_Call, and AOF client */ |
| 4841 | + ClientReplRDBOnly ClientFlags = 1 << 42 /* This client is a replica that only wants RDB without replication buffer. */ |
| 4842 | + ClientNoEvict ClientFlags = 1 << 43 /* This client is protected against client memory eviction. */ |
| 4843 | + ClientAllowOOM ClientFlags = 1 << 44 /* Client used by RM_Call is allowed to fully execute scripts even when in OOM */ |
| 4844 | + ClientNoTouch ClientFlags = 1 << 45 /* This client will not touch LFU/LRU stats. */ |
| 4845 | + ClientPushing ClientFlags = 1 << 46 /* This client is pushing notifications. */ |
| 4846 | +) |
| 4847 | + |
| 4848 | +// ClientInfo is redis-server ClientInfo, not go-redis *Client |
| 4849 | +type ClientInfo struct { |
| 4850 | + ID int64 // redis version 2.8.12, a unique 64-bit client ID |
| 4851 | + Addr string // address/port of the client |
| 4852 | + LAddr string // address/port of local address client connected to (bind address) |
| 4853 | + FD int64 // file descriptor corresponding to the socket |
| 4854 | + Name string // the name set by the client with CLIENT SETNAME |
| 4855 | + Age time.Duration // total duration of the connection in seconds |
| 4856 | + Idle time.Duration // idle time of the connection in seconds |
| 4857 | + Flags ClientFlags // client flags (see below) |
| 4858 | + DB int // current database ID |
| 4859 | + Sub int // number of channel subscriptions |
| 4860 | + PSub int // number of pattern matching subscriptions |
| 4861 | + SSub int // redis version 7.0.3, number of shard channel subscriptions |
| 4862 | + Multi int // number of commands in a MULTI/EXEC context |
| 4863 | + QueryBuf int // qbuf, query buffer length (0 means no query pending) |
| 4864 | + QueryBufFree int // qbuf-free, free space of the query buffer (0 means the buffer is full) |
| 4865 | + ArgvMem int // incomplete arguments for the next command (already extracted from query buffer) |
| 4866 | + MultiMem int // redis version 7.0, memory is used up by buffered multi commands |
| 4867 | + BufferSize int // rbs, usable size of buffer |
| 4868 | + BufferPeak int // rbp, peak used size of buffer in last 5 sec interval |
| 4869 | + OutputBufferLength int // obl, output buffer length |
| 4870 | + OutputListLength int // oll, output list length (replies are queued in this list when the buffer is full) |
| 4871 | + OutputMemory int // omem, output buffer memory usage |
| 4872 | + TotalMemory int // tot-mem, total memory consumed by this client in its various buffers |
| 4873 | + Events string // file descriptor events (see below) |
| 4874 | + LastCmd string // cmd, last command played |
| 4875 | + User string // the authenticated username of the client |
| 4876 | + Redir int64 // client id of current client tracking redirection |
| 4877 | + Resp int // redis version 7.0, client RESP protocol version |
| 4878 | + LibName string // redis version 7.2, client library name |
| 4879 | + LibVer string // redis version 7.2, client library version |
| 4880 | +} |
| 4881 | + |
| 4882 | +type ClientInfoCmd struct { |
| 4883 | + baseCmd |
| 4884 | + |
| 4885 | + val *ClientInfo |
| 4886 | +} |
| 4887 | + |
| 4888 | +var _ Cmder = (*ClientInfoCmd)(nil) |
| 4889 | + |
| 4890 | +func NewClientInfoCmd(ctx context.Context, args ...interface{}) *ClientInfoCmd { |
| 4891 | + return &ClientInfoCmd{ |
| 4892 | + baseCmd: baseCmd{ |
| 4893 | + ctx: ctx, |
| 4894 | + args: args, |
| 4895 | + }, |
| 4896 | + } |
| 4897 | +} |
| 4898 | + |
| 4899 | +func (cmd *ClientInfoCmd) SetVal(val *ClientInfo) { |
| 4900 | + cmd.val = val |
| 4901 | +} |
| 4902 | + |
| 4903 | +func (cmd *ClientInfoCmd) String() string { |
| 4904 | + return cmdString(cmd, cmd.val) |
| 4905 | +} |
| 4906 | + |
| 4907 | +func (cmd *ClientInfoCmd) Val() *ClientInfo { |
| 4908 | + return cmd.val |
| 4909 | +} |
| 4910 | + |
| 4911 | +func (cmd *ClientInfoCmd) Result() (*ClientInfo, error) { |
| 4912 | + return cmd.val, cmd.err |
| 4913 | +} |
| 4914 | + |
| 4915 | +func (cmd *ClientInfoCmd) readReply(rd *proto.Reader) (err error) { |
| 4916 | + txt, err := rd.ReadString() |
| 4917 | + if err != nil { |
| 4918 | + return err |
| 4919 | + } |
| 4920 | + |
| 4921 | + // sds o = catClientInfoString(sdsempty(), c); |
| 4922 | + // o = sdscatlen(o,"\n",1); |
| 4923 | + // addReplyVerbatim(c,o,sdslen(o),"txt"); |
| 4924 | + // sdsfree(o); |
| 4925 | + cmd.val, err = parseClientInfo(strings.TrimSpace(txt)) |
| 4926 | + return err |
| 4927 | +} |
| 4928 | + |
| 4929 | +// fmt.Sscanf() cannot handle null values |
| 4930 | +func parseClientInfo(txt string) (info *ClientInfo, err error) { |
| 4931 | + info = &ClientInfo{} |
| 4932 | + for _, s := range strings.Split(txt, " ") { |
| 4933 | + kv := strings.Split(s, "=") |
| 4934 | + if len(kv) != 2 { |
| 4935 | + return nil, fmt.Errorf("redis: unexpected client info data (%s)", s) |
| 4936 | + } |
| 4937 | + key, val := kv[0], kv[1] |
| 4938 | + |
| 4939 | + switch key { |
| 4940 | + case "id": |
| 4941 | + info.ID, err = strconv.ParseInt(val, 10, 64) |
| 4942 | + case "addr": |
| 4943 | + info.Addr = val |
| 4944 | + case "laddr": |
| 4945 | + info.LAddr = val |
| 4946 | + case "fd": |
| 4947 | + info.FD, err = strconv.ParseInt(val, 10, 64) |
| 4948 | + case "name": |
| 4949 | + info.Name = val |
| 4950 | + case "age": |
| 4951 | + var age int |
| 4952 | + if age, err = strconv.Atoi(val); err == nil { |
| 4953 | + info.Age = time.Duration(age) * time.Second |
| 4954 | + } |
| 4955 | + case "idle": |
| 4956 | + var idle int |
| 4957 | + if idle, err = strconv.Atoi(val); err == nil { |
| 4958 | + info.Idle = time.Duration(idle) * time.Second |
| 4959 | + } |
| 4960 | + case "flags": |
| 4961 | + if val == "N" { |
| 4962 | + break |
| 4963 | + } |
| 4964 | + |
| 4965 | + for i := 0; i < len(val); i++ { |
| 4966 | + switch val[i] { |
| 4967 | + case 'S': |
| 4968 | + info.Flags |= ClientSlave |
| 4969 | + case 'O': |
| 4970 | + info.Flags |= ClientSlave | ClientMonitor |
| 4971 | + case 'M': |
| 4972 | + info.Flags |= ClientMaster |
| 4973 | + case 'P': |
| 4974 | + info.Flags |= ClientPubSub |
| 4975 | + case 'x': |
| 4976 | + info.Flags |= ClientMulti |
| 4977 | + case 'b': |
| 4978 | + info.Flags |= ClientBlocked |
| 4979 | + case 't': |
| 4980 | + info.Flags |= ClientTracking |
| 4981 | + case 'R': |
| 4982 | + info.Flags |= ClientTrackingBrokenRedir |
| 4983 | + case 'B': |
| 4984 | + info.Flags |= ClientTrackingBCAST |
| 4985 | + case 'd': |
| 4986 | + info.Flags |= ClientDirtyCAS |
| 4987 | + case 'c': |
| 4988 | + info.Flags |= ClientCloseAfterCommand |
| 4989 | + case 'u': |
| 4990 | + info.Flags |= ClientUnBlocked |
| 4991 | + case 'A': |
| 4992 | + info.Flags |= ClientCloseASAP |
| 4993 | + case 'U': |
| 4994 | + info.Flags |= ClientUnixSocket |
| 4995 | + case 'r': |
| 4996 | + info.Flags |= ClientReadOnly |
| 4997 | + case 'e': |
| 4998 | + info.Flags |= ClientNoEvict |
| 4999 | + case 'T': |
| 5000 | + info.Flags |= ClientNoTouch |
| 5001 | + default: |
| 5002 | + return nil, fmt.Errorf("redis: unexpected client info flags(%s)", string(val[i])) |
| 5003 | + } |
| 5004 | + } |
| 5005 | + case "db": |
| 5006 | + info.DB, err = strconv.Atoi(val) |
| 5007 | + case "sub": |
| 5008 | + info.Sub, err = strconv.Atoi(val) |
| 5009 | + case "psub": |
| 5010 | + info.PSub, err = strconv.Atoi(val) |
| 5011 | + case "ssub": |
| 5012 | + info.SSub, err = strconv.Atoi(val) |
| 5013 | + case "multi": |
| 5014 | + info.Multi, err = strconv.Atoi(val) |
| 5015 | + case "qbuf": |
| 5016 | + info.QueryBuf, err = strconv.Atoi(val) |
| 5017 | + case "qbuf-free": |
| 5018 | + info.QueryBufFree, err = strconv.Atoi(val) |
| 5019 | + case "argv-mem": |
| 5020 | + info.ArgvMem, err = strconv.Atoi(val) |
| 5021 | + case "multi-mem": |
| 5022 | + info.MultiMem, err = strconv.Atoi(val) |
| 5023 | + case "rbs": |
| 5024 | + info.BufferSize, err = strconv.Atoi(val) |
| 5025 | + case "rbp": |
| 5026 | + info.BufferPeak, err = strconv.Atoi(val) |
| 5027 | + case "obl": |
| 5028 | + info.OutputBufferLength, err = strconv.Atoi(val) |
| 5029 | + case "oll": |
| 5030 | + info.OutputListLength, err = strconv.Atoi(val) |
| 5031 | + case "omem": |
| 5032 | + info.OutputMemory, err = strconv.Atoi(val) |
| 5033 | + case "tot-mem": |
| 5034 | + info.TotalMemory, err = strconv.Atoi(val) |
| 5035 | + case "events": |
| 5036 | + info.Events = val |
| 5037 | + case "cmd": |
| 5038 | + info.LastCmd = val |
| 5039 | + case "user": |
| 5040 | + info.User = val |
| 5041 | + case "redir": |
| 5042 | + info.Redir, err = strconv.ParseInt(val, 10, 64) |
| 5043 | + case "resp": |
| 5044 | + info.Resp, err = strconv.Atoi(val) |
| 5045 | + case "lib-name": |
| 5046 | + info.LibName = val |
| 5047 | + case "lib-ver": |
| 5048 | + info.LibVer = val |
| 5049 | + default: |
| 5050 | + return nil, fmt.Errorf("redis: unexpected client info key(%s)", key) |
| 5051 | + } |
| 5052 | + |
| 5053 | + if err != nil { |
| 5054 | + return nil, err |
| 5055 | + } |
| 5056 | + } |
| 5057 | + |
| 5058 | + return info, nil |
| 5059 | +} |
0 commit comments