|
| 1 | +package gorma |
| 2 | + |
| 3 | +import ( |
| 4 | + "os" |
| 5 | + "fmt" |
| 6 | + "sort" |
| 7 | + "strconv" |
| 8 | + "strings" |
| 9 | + "github.com/hhxsv5/go-redis-memory-analysis/storages" |
| 10 | +) |
| 11 | + |
| 12 | +type AnalysisConnection struct { |
| 13 | + redis *storages.RedisClient |
| 14 | + Reports DBReports |
| 15 | +} |
| 16 | + |
| 17 | +func NewAnalysisConnection(host string, port uint16, password string) (*AnalysisConnection, error) { |
| 18 | + redis, err := storages.NewRedisClient(host, port, password) |
| 19 | + if err != nil { |
| 20 | + return nil, err |
| 21 | + } |
| 22 | + return &AnalysisConnection{redis, DBReports{}}, nil |
| 23 | +} |
| 24 | + |
| 25 | +func (analysis *AnalysisConnection) Close() { |
| 26 | + if analysis.redis != nil { |
| 27 | + analysis.redis.Close() |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +func (analysis AnalysisConnection) Start(delimiters []string) { |
| 32 | + fmt.Println("Starting analysis") |
| 33 | + match := "*[" + strings.Join(delimiters, "") + "]*" |
| 34 | + databases, _ := analysis.redis.GetDatabases() |
| 35 | + |
| 36 | + var ( |
| 37 | + cursor uint64 |
| 38 | + r Report |
| 39 | + f float64 |
| 40 | + ttl int64 |
| 41 | + length uint64 |
| 42 | + sr SortBySizeReports |
| 43 | + mr KeyReports |
| 44 | + ) |
| 45 | + |
| 46 | + for db, _ := range databases { |
| 47 | + fmt.Println("Analyzing db", db) |
| 48 | + cursor = 0 |
| 49 | + mr = KeyReports{} |
| 50 | + |
| 51 | + analysis.redis.Select(db) |
| 52 | + |
| 53 | + for { |
| 54 | + keys, _ := analysis.redis.Scan(&cursor, match, 3000) |
| 55 | + fd, fp, tmp, nk := "", 0, 0, "" |
| 56 | + for _, key := range keys { |
| 57 | + fd, fp, tmp, nk, ttl = "", 0, 0, "", 0 |
| 58 | + for _, delimiter := range delimiters { |
| 59 | + tmp = strings.Index(key, delimiter) |
| 60 | + if tmp != -1 && (tmp < fp || fp == 0) { |
| 61 | + fd, fp = delimiter, tmp |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + if fp == 0 { |
| 66 | + continue |
| 67 | + } |
| 68 | + |
| 69 | + nk = key[0:fp] + fd + "*" |
| 70 | + |
| 71 | + if _, ok := mr[nk]; ok { |
| 72 | + r = mr[nk] |
| 73 | + } else { |
| 74 | + r = Report{nk, 0, 0, 0, 0} |
| 75 | + } |
| 76 | + |
| 77 | + ttl, _ = analysis.redis.Ttl(key) |
| 78 | + |
| 79 | + switch ttl { |
| 80 | + case -2: |
| 81 | + continue |
| 82 | + case -1: |
| 83 | + r.NeverExpire++ |
| 84 | + r.Count++ |
| 85 | + default: |
| 86 | + f = float64(r.AvgTtl*(r.Count-r.NeverExpire)+uint64(ttl)) / float64(r.Count+1-r.NeverExpire) |
| 87 | + ttl, _ := strconv.ParseUint(fmt.Sprintf("%0.0f", f), 10, 64) |
| 88 | + r.AvgTtl = ttl |
| 89 | + r.Count++ |
| 90 | + } |
| 91 | + |
| 92 | + length, _ = analysis.redis.SerializedLength(key) |
| 93 | + r.Size += length |
| 94 | + |
| 95 | + mr[nk] = r |
| 96 | + } |
| 97 | + |
| 98 | + if cursor == 0 { |
| 99 | + break |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + //Sort by size |
| 104 | + sr = SortBySizeReports{} |
| 105 | + for _, report := range mr { |
| 106 | + sr = append(sr, report) |
| 107 | + } |
| 108 | + sort.Sort(sr) |
| 109 | + |
| 110 | + analysis.Reports[db] = sr |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +func (analysis AnalysisConnection) SaveReports(folder string) error { |
| 115 | + fmt.Println("Saving the results of the analysis into", folder) |
| 116 | + if _, err := os.Stat(folder); os.IsNotExist(err) { |
| 117 | + os.MkdirAll(folder, os.ModePerm) |
| 118 | + } |
| 119 | + |
| 120 | + var ( |
| 121 | + str string |
| 122 | + filename string |
| 123 | + size float64 |
| 124 | + unit string |
| 125 | + ) |
| 126 | + template := fmt.Sprintf("%s%sredis-analysis-%s%s", folder, string(os.PathSeparator), strings.Replace(analysis.redis.Id, ":", "-", -1), "-%d.csv") |
| 127 | + for db, reports := range analysis.Reports { |
| 128 | + filename = fmt.Sprintf(template, db) |
| 129 | + fp, err := storages.NewFile(filename, os.O_CREATE|os.O_WRONLY, os.ModePerm) |
| 130 | + if err != nil { |
| 131 | + return err |
| 132 | + } |
| 133 | + fp.Append([]byte("Key,Count,Size,NeverExpire,AvgTtl(excluded never expire)\n")) |
| 134 | + for _, value := range reports { |
| 135 | + size, unit = HumanSize(value.Size) |
| 136 | + str = fmt.Sprintf("%s,%d,%s,%d,%d\n", |
| 137 | + value.Key, |
| 138 | + value.Count, |
| 139 | + fmt.Sprintf("%0.3f %s", size, unit), |
| 140 | + value.NeverExpire, |
| 141 | + value.AvgTtl) |
| 142 | + fp.Append([]byte(str)) |
| 143 | + } |
| 144 | + fp.Close() |
| 145 | + } |
| 146 | + return nil |
| 147 | +} |
0 commit comments