Blog

Notes on the .Net ecosystem, Angular, Software Architecture, Machine Learning and CyberSecurity

← All posts
No database: how RockPlayer avoids asking the music provider twice
  • Redis
  • Caching

No database: how RockPlayer avoids asking the music provider twice

RockRecommender already avoids repeating a song on its own, keeping a history of what has already been shown to each person. So why does RockPlayer also need some memory? Because every time a new song is recommended, someone on the other end needs to ask YouTube where to play it, and that question is expensive.

The cost of asking again

The YouTube API used here (more details in article 6) has a daily quota. If two different people ask for the same song, or if the same person runs into the same recommendation again after the catalog runs out (RockRecommender allows this as a last resort, not as a rule), asking YouTube again to find exactly the same video is a waste.

Why it's not just about using a database

A database is built to store information that needs to last and be queried in varied ways. What RockPlayer needs to store here is much simpler: an answer that has already been calculated, with an expiration date. It doesn't need an index, it doesn't need a relation to another table, it just needs to be there quickly when someone asks again, and disappear on its own after a while.

Redis: what it is, and why it fits here

Redis keeps everything in memory, and has native key expiration, you say how long a value should live, and it disappears on its own, with no cleanup routine needed.

The advantages

Speed, because it lives in memory. Expiration for free, with no need to write a single line of code to delete old data. And it's simple to run, just one more container in the same docker-compose that already brings up RockRecommender's MongoDB.

The disadvantages (because every tool has them)

It's not durable by default, if Redis restarts, the cache empties out, and that's fine, because it was never the source of truth. And it doesn't replace a real database, if RockPlayer ever needs to store something that really needs to survive a restart, Redis is not the right tool for that.

How this fits into the architecture

An interface hides Redis from the rest of the system, the same way IMusicProviderAdapter hides the music provider:

public interface IProviderLookupCache
{
    Task<PlaybackSource?> GetAsync(Guid songId);
    Task SetAsync(Guid songId, PlaybackSource source);
}

GetAsync returns null when it finds nothing, because that is not an error, it is just saying that this answer isn't stored yet. Whoever calls this interface (the orchestrator, in article 5) decides what to do with that null, look it up on the real music provider and store the result for next time. The next article closes out the theory, with what really changed in Angular 22.

Sources