diff --git a/pkg/cache.go b/pkg/cache.go new file mode 100644 index 0000000..c900af2 --- /dev/null +++ b/pkg/cache.go @@ -0,0 +1,55 @@ +package pkg + +import ( + "sync" + "time" + + "google.golang.org/api/youtube/v3" +) + +const expiry = 24 * time.Hour + +type item struct { + Updated time.Time + Subs []*youtube.Subscription +} + +var ( + subsCache = make(map[string]*item) + subsCacheLock sync.RWMutex +) + +func readSubsCache(key string) []*youtube.Subscription { + subsCacheLock.RLock() + defer subsCacheLock.RUnlock() + + item := subsCache[key] + if item == nil { + return nil + } + + if !item.Updated.After(time.Now().Add(-expiry)) { + return nil + } + + subsCopy := make([]*youtube.Subscription, len(item.Subs)) + copy(subsCopy, item.Subs) + return subsCopy +} + +func storeSubsCache(key string, subs []*youtube.Subscription) { + subsCacheLock.Lock() + defer subsCacheLock.Unlock() + + if subs == nil { + subsCache[key] = nil + return + } + + subsCopy := make([]*youtube.Subscription, len(subs)) + copy(subsCopy, subs) + subsCache[key] = &item{ + Updated: time.Now(), + Subs: subsCopy, + } +} diff --git a/pkg/feed.go b/pkg/feed.go index 0ef74e2..2fd799b 100644 --- a/pkg/feed.go +++ b/pkg/feed.go @@ -5,7 +5,6 @@ import ( "net/http" "go.uber.org/zap" - "google.golang.org/api/youtube/v3" ) const ( @@ -31,39 +30,42 @@ func ExportHandler(w http.ResponseWriter, r *http.Request) { return } - yt, err := ytServiceFromRefreshToken(r.Context(), refreshToken) - if err != nil { - http.Error(w, err.Error(), http.StatusUnauthorized) - return - } - - var items []*youtube.Subscription - - var pageToken string - for { - logger.Info("making YouTube API request", zap.String("page_token", pageToken)) - - resp, err := yt.Subscriptions. - List("snippet"). - MaxResults(50). - Order("alphabetical"). - Mine(true). - PageToken(pageToken). - Do() + items := readSubsCache(refreshToken) + if len(items) <= 0 { + yt, err := ytServiceFromRefreshToken(r.Context(), refreshToken) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, err.Error(), http.StatusUnauthorized) return } - items = append(items, resp.Items...) + var pageToken string + for { + logger.Info("making YouTube API request", zap.String("page_token", pageToken)) - pageToken = resp.NextPageToken - if resp.NextPageToken == "" { - break + resp, err := yt.Subscriptions. + List("snippet"). + MaxResults(50). + Order("alphabetical"). + Mine(true). + PageToken(pageToken). + Do() + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + items = append(items, resp.Items...) + + pageToken = resp.NextPageToken + if resp.NextPageToken == "" { + break + } } - } - logger.Info("found items", zap.Int("amount", len(items))) + logger.Info("received item from API", zap.Int("amount", len(items))) + + storeSubsCache(refreshToken, items) + } var result string for _, item := range items {