]> git.lizzy.rs Git - go-anidb.git/blobdiff - episodecache.go
anidb: Log UDP traffic
[go-anidb.git] / episodecache.go
index ff177770a12f50046996c4d7fb415927d080f00f..3c3a91e18310d5182dbabb522b181338251acce1 100644 (file)
@@ -4,7 +4,6 @@ import (
        "encoding/gob"
        "strconv"
        "strings"
-       "sync"
        "time"
 )
 
@@ -23,44 +22,52 @@ func (e *Episode) IsStale() bool {
        return time.Now().Sub(e.Cached) > EpisodeCacheDuration
 }
 
-var eidAidMap = map[EID]AID{}
-var eidAidLock = sync.RWMutex{}
-
 // Unique Episode IDentifier.
 type EID int
 
 // Retrieves the Episode corresponding to this EID from the cache.
 func (eid EID) Episode() *Episode {
-       e, _ := caches.Get(episodeCache).Get(int(eid)).(*Episode)
-       return e
+       var e Episode
+       if cache.Get(&e, "eid", eid) == nil {
+               return &e
+       }
+       return nil
 }
 
 func cacheEpisode(ep *Episode) {
-       eidAidLock.Lock()
-       defer eidAidLock.Unlock()
-
-       eidAidMap[ep.EID] = ep.AID
-       caches.Get(episodeCache).Set(int(ep.EID), ep)
+       cache.Set(ep.AID, "aid", "by-eid", ep.EID)
+       cache.Set(ep, "eid", ep.EID)
 }
 
-// Retrieves the Episode from the cache if possible.
+// Retrieves an Episode by its EID.
 //
-// If the result is stale, then queries the UDP API to
-// know which AID owns this EID, then gets the episodes
-// from the Anime.
+// If we know which AID owns this EID, then it's equivalent
+// to an Anime query. Otherwise, uses both the HTTP and UDP
+// APIs to retrieve it.
 func (adb *AniDB) EpisodeByID(eid EID) <-chan *Episode {
+       keys := []cacheKey{"eid", eid}
        ch := make(chan *Episode, 1)
 
-       if e := eid.Episode(); e != nil && !e.IsStale() {
-               ch <- e
+       if eid < 1 {
+               ch <- nil
                close(ch)
                return ch
        }
 
-       ec := caches.Get(episodeCache)
        ic := make(chan Cacheable, 1)
        go func() { ch <- (<-ic).(*Episode); close(ch) }()
-       if ec.Intent(int(eid), ic) {
+       if intentMap.Intent(ic, keys...) {
+               return ch
+       }
+
+       if !cache.CheckValid(keys...) {
+               intentMap.NotifyClose((*Episode)(nil), keys...)
+               return ch
+       }
+
+       e := eid.Episode()
+       if !e.IsStale() {
+               intentMap.NotifyClose(e, keys...)
                return ch
        }
 
@@ -68,13 +75,11 @@ func (adb *AniDB) EpisodeByID(eid EID) <-chan *Episode {
                // The UDP API data is worse than the HTTP API anime data,
                // try and get from the corresponding Anime
 
-               eidAidLock.RLock()
-               aid, ok := eidAidMap[eid]
-               eidAidLock.RUnlock()
+               aid := AID(0)
+               ok := cache.Get(&aid, "aid", "by-eid", eid) == nil
 
                udpDone := false
 
-               var e *Episode
                for i := 0; i < 2; i++ {
                        if !ok && udpDone {
                                // couldn't get anime and we already ran the EPISODE query
@@ -91,27 +96,47 @@ func (adb *AniDB) EpisodeByID(eid EID) <-chan *Episode {
                                        if id, err := strconv.ParseInt(parts[1], 10, 32); err == nil {
                                                ok = true
                                                aid = AID(id)
+                                       } else {
+                                               break
                                        }
+                               } else if reply.Code() == 340 {
+                                       cache.MarkInvalid(keys...)
+                                       cache.Delete(keys...) // deleted EID?
+                                       break
                                } else {
                                        break
                                }
                                udpDone = true
                        }
-                       <-adb.AnimeByID(AID(aid)) // this caches episodes...
-                       e = eid.Episode()         // ...so this is now a cache hit
+                       a := <-adb.AnimeByID(AID(aid)) // this caches episodes...
+                       ep := eid.Episode()            // ...so this is now a cache hit
 
-                       if e != nil {
+                       if !ep.IsStale() {
+                               e = ep
                                break
                        } else {
-                               // if this is somehow still a miss, then the EID<->AID map broke
-                               eidAidLock.Lock()
-                               delete(eidAidMap, eid)
-                               eidAidLock.Unlock()
+                               // check to see if we looked in the right AID
+                               found := false
+                               if a != nil {
+                                       for _, ep := range a.Episodes {
+                                               if eid == ep.EID {
+                                                       found = true
+                                                       break
+                                               }
+                                       }
+                               }
 
-                               ok = false
+                               // if found, then it's just that the anime is also stale (offline?)
+                               if found {
+                                       break
+                               } else {
+                                       // otherwise, the EID<->AID map broke
+                                       ok = false
+                                       cache.Delete("aid", "by-eid", eid)
+                               }
                        }
                }
-               // Caching (and channel broadcasting) done by AnimeByID
+               intentMap.NotifyClose(e, keys...)
        }()
        return ch
 }