Scenic.Cache design question

Design question.

I’m looking at changing the Cache module in Scenic. The cache is for fast access to static assets such as fonts, textures, font_metrics, etc.

The key, so far, is a hash of the contents of the row, which is nice on multiple levels. I’ve been running into problems tho where I’d like to know what type of data is in the row without necessarily inspecting it directly.

Am currently playing to two possible design choices and I’d like feedback. I could.

a) change the key to something like {:font, hash} | {:texture, hash} etc. This is nice in that I can tell what type of content is in the row just by inspecting the key. It adds complexity tho, and continues overloading the backing :ets table with multiple types of content.

or

b) I could have multiple caches, one for each type. Cache.Textures, Cache.Fonts, etc. Each would have only one type of content, which is described by the name of the module. However, it opens multiple :ets tables, spawns multiple cache processes, etc.

I’m looking for good arguments either way. I’ve gone back and forth several times. There are other factors as well such as some processes (drivers) want to subscribe to those caches, but only care about some data types and not others. This leads me to lean slightly toward option b.

Thoughts?

6 Likes

I haven’t worked with Scenic.Cache directly (yet), but my preference goes to option A, as with B someone might want to cache a content type for which you have no implementation yet. Having a cache per content type feels a bit overkill if every cache has the same behaviour/functionality?

1 Like

I think b is the best solution for Scenic’s needs here, given my rough understanding of the problem domain and source code. I’m operating under the assumptions that:

  1. The set of asset types correlate closely to Scenic and WebGL primitives and are effectively closed to extension
  2. The cache content is private to the framework
  3. Different asset types may need different attributes
  4. The cache supervision tree initialization and configuration is private to the framework
  5. Different asset types have reasonably-anticipatable different read-write access patterns

The way I see it, as Scenic grows, a) might accommodate more naive user-land asset-type flexibility but that is entirely neutered by point 1. On the other hand, b) affords more asset-type specificity and specialization, and the framework may want to take more advantage of that as it discovers asset utilization patterns to harness the properties of 2.-5.

If these assumptions are correct, we are talking about constrained types in highly framework-controlled code and a memory-and-performance-sensitive problem space. The b) implementation lends itself to individual asset-specific domain-knowledge optimizations in structure, supervision, and ETS selecting; which seems like the direction the cache code would drift towards with or without being designed for specialization.

1 Like

Thank you.

I’m going with mostly-b approach, although with changes.

There will be a cache-per-type running as separate processes and :ets tables. However, almost all the code is encapsulated in a common base module, so making new custom types is super easy.

Like this:

defmodule Scenic.Cache.Static.Texture do
  use Scenic.Cache.Base, name: "texture", static: true
end

Am in the process of adding the ability for the specific types to have custom loaders, which is just a parser… This way I get the benefit of the dedicated cache types, but still make it super easy to make new ones.

The biggest drawback is that this will be a breaking change for anyone that has gone deep in cache use. Not a huge breaking change, but upgrading to v.10 will be a small amount of work for some.

2 Likes

Also, as you can see from the static true/false option, I’m going to get way more aware of whether or not the contents of the cache is static or not. This will be super important as the online relay service starts working.

2 Likes