|
- package main
-
- import (
- "encoding/json"
- "fmt"
- "net/http"
- "strconv"
- "strings"
- "time"
-
- "github.com/go-redis/redis/v8"
- )
-
- type LastAccessStatsKey struct {
- Project string
- Shard string
- SubShard string
- }
-
- type LastAccessStatsEntry struct {
- First time.Time `json:"first"`
- Last time.Time `json:"last"`
- Size int64 `json:"size"`
- }
-
- type LastAccessStatsMap map[LastAccessStatsKey]*LastAccessStatsEntry
-
- func (that LastAccessStatsMap) MarshalJSON() ([]byte, error) {
- mapped := map[string]map[string]any{}
- for key, value := range that {
- mapped[fmt.Sprintf("%s:%s:%s", key.Project, key.Shard, key.SubShard)] = map[string]any{
- "first": value.First.Format(time.RFC3339),
- "last": value.Last.Format(time.RFC3339),
- "size": value.Size,
- }
- }
- return json.Marshal(mapped)
- }
-
- func LastAccessStatsKeyFromString(s string) (LastAccessStatsKey, error) {
- parts := strings.SplitN(s, ":", 3)
- if len(parts) != 3 {
- return LastAccessStatsKey{}, fmt.Errorf("invalid key: %s", s)
- }
- return LastAccessStatsKey{
- Project: parts[0],
- Shard: parts[1],
- SubShard: parts[2],
- }, nil
- }
-
- func (that *GlobalBackfeedManager) HandleLastAccessStats(res http.ResponseWriter, req *http.Request) {
- defer req.Body.Close()
- merge := map[string]bool{}
- if vv, ok := req.URL.Query()["merge"]; ok {
- for _, v := range vv {
- merge[v] = true
- }
- }
- lastTs, err := that.BackfeedRedis.HGetAll(req.Context(), ":last_ts").Result()
- if err != nil && err != redis.Nil {
- WriteResponse(res, http.StatusInternalServerError, err)
- return
- }
- memoryUsages := map[string]*redis.IntCmd{}
- pipe := that.BackfeedRedis.Pipeline()
- for key := range lastTs {
- memoryUsages[key] = pipe.MemoryUsage(req.Context(), key)
- }
- _, err = pipe.Exec(req.Context())
- if err != nil && err != redis.Nil {
- WriteResponse(res, http.StatusInternalServerError, err)
- return
- }
- lastAccessStats := LastAccessStatsMap{}
- for key, value := range lastTs {
- // value is in unix timestamp format
- ts, err := strconv.ParseInt(value, 10, 64)
- if err != nil {
- WriteResponse(res, http.StatusInternalServerError, err)
- return
- }
- lastAccessStatsKey, err := LastAccessStatsKeyFromString(key)
- if err != nil {
- WriteResponse(res, http.StatusInternalServerError, err)
- return
- }
- if merge["project"] {
- lastAccessStatsKey.Project = "*"
- }
- if merge["shard"] {
- lastAccessStatsKey.Shard = "*"
- }
- if merge["sub_shard"] {
- lastAccessStatsKey.SubShard = "*"
- }
- parsedTs := time.Unix(ts, 0)
- if v, has := lastAccessStats[lastAccessStatsKey]; !has {
- lastAccessStats[lastAccessStatsKey] = &LastAccessStatsEntry{
- First: parsedTs,
- Last: parsedTs,
- Size: memoryUsages[key].Val(),
- }
- } else {
- if v.First.After(parsedTs) {
- v.First = parsedTs
- }
- if v.Last.Before(parsedTs) {
- v.Last = parsedTs
- }
- v.Size += memoryUsages[key].Val()
- }
- }
- WriteResponse(res, http.StatusOK, lastAccessStats)
- }
|