Skip to content

Tiered cache class #1

@Sv443

Description

@Sv443

Would be cool to have some kind of cache class that can have multiple tiers with different max TTLs, with data being moved between tiers based on what is fetched the most.
Also beneficial would be a DataStore / DataStoreSerializer integration with compression support for persistently saving the cache in an efficient way.

Brianstorming
const lyricsUrlCache = new TieredCache({
  id: "lyricsUrl",
  tiers: [
    // L0: memory
    // loaded into memory on startup, loaded from memory on cache request
    // persisted to memory and GM storage on cache update
    {
      engine: "memory",
      persist: "GM",
      staleOptions: {
        // if any of these are true, the oldest entry is moved to a lower tier
        ttl: 60 * 60 * 24, // 1 day
        amount: 100,
      },
    },
    // L1: GM storage
    // loaded from GM storage on cache request
    // persisted to GM storage on cache update
    {
      engine: "GM",
      persist: "GM",
      staleOptions: {
        // since there's no tier below, the oldest entry will be deleted
        ttl: 60 * 60 * 24 * 90, // 90 days
        amount: 25_000,
      },
      // compress before storing and decompress after loading
      encodeData: (data) => compress(data, "deflate", "string"),
      decodeData: (data) => decompress(data, "deflate", "string"),
    }
  ],
  async fetchAction(artists: string, title: string) {
    const res = await fetch(
      `https://api.sv443.net/geniurl/search/top?q=${encodeURIComponent(artists)}-${encodeURIComponent(title)}`,
      {
        method: "GET",
        headers: {
          "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
        }
      }
    );
    if(res.status >= 300 || res.status < 200)
      throw new Error(`Failed to fetch lyrics URL: ${res.statusText}`);

    const data = await res.json();

    if(data && "error" in data && !data.error && "url" in data && typeof data.url === "string")
      return {
        key: [artists, title],
        data: data.url,
        maxTTL: 60 * 60 * 24 * 60, // 60 days
      };
    else
      throw new Error("No URL found in response");
  }
});

// usage:

async function fetchLyricsUrl(artists: string, title: string) {
  // fetch from cache, else fetch via XHR
  const lyricsUrl = await lyricsUrlCache.get(artists, title);
}


// TieredCache data storage overview:
/*

_uu_cache_lyricsUrl: [
  {
    k: "artist1-title1",                    // key / compound key
    d: "https://genius.com/artist1-title1", // data
    c: 1699999999999,                       // created timestamp
    e: 1699999999999,                       // edited timestamp
    a: 1699999999999,                       // last accessed timestamp
    r: 17,                                  // reads amount
  }
],

*/

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions