Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add interface for cache? #10

Open
jasonmccallister opened this issue Nov 26, 2020 · 12 comments
Open

Add interface for cache? #10

jasonmccallister opened this issue Nov 26, 2020 · 12 comments

Comments

@jasonmccallister
Copy link

When I was looking to start working on a similar module, I stumbled across this one and I love the work that is being done on this module, this is going to be a great addition to the Caddy modules list!

The only thing I was wondering is if there could be an interface for the caching backend, similar to the certmagic storage interface so the default storage, olric, could be swapped out with something like redis?

@mholt
Copy link
Member

mholt commented Nov 26, 2020

That's a cool idea, but sounds tricky; the cache has to meet certain criteria to be useful (and I'm not sure even olrich supports all of the features/functionality we want/require yet). What interface do you propose?

@francislavoie
Copy link
Member

Might be really hard to abstract out the relevant interfaces... probably better to just have a separate plugin that is a redis version.

@mholt
Copy link
Member

mholt commented Nov 26, 2020

Or not :) We don't know how hard it is until we try. What are the required functions of a cache module?

@jasonmccallister
Copy link
Author

I think the interface would match up to something along the lines of the following:

type Cacher interface {
	// Put will create a new cache item if it exists or not.
	Put(key string, content []byte, duration time.Duration) error

	// Add will add a cache item to the cache if the key does not
	// exist.
	Add(key string, content []byte, duration time.Duration) error

	// Get returns a item from the cache or an error if the item
	// does not exist
	Get(key string) ([]byte, error)

	// Forget removes an item from the cache, if the item cannot be
	// removed it will return an error.
	Forget(key string) error
}

This is heavily inspired by the Laravel Cache interface, which is amazing, minus a few methods we might not want/need.

@dunglas
Copy link
Collaborator

dunglas commented Nov 28, 2020

We also need to be able to retrieve the TTL of an entry (for the Cache-Status header). We'll also probably need a tag mechanism at some point to support invalidation / purging.

@darkweak
Copy link
Collaborator

Already available in Souin https://github.com/Darkweak/Souin btw

@soyuka
Copy link
Contributor

soyuka commented Apr 1, 2021

If I may, I think that https://www.php-fig.org/psr/psr-6/ fits the model in many ways:

  • Pool: The Pool represents a collection of items in a caching system. The pool is a logical Repository of all items it contains. All cacheable items are retrieved from the Pool as an Item object, and all interaction with the whole universe of cached objects happens through the Pool.
  • Item An Item represents a single key/value pair within a Pool. The key is the primary unique identifier for an Item and MUST be immutable. The Value MAY be changed at any time.

We may also want an Adapter interface implements the caching mechanism to store the information using a transport (fs, database, redis, olric etc.) which would give access to the Pool.

In conjunction with a few reads, we can improve the above model to:

type CacheItem interface {
  GetKey() string
  Get() []byte
  IsHit() bool
  Set(value []byte)
  SetTTL(duration time.Duration)
  GetTTL() time.Duration
}

/// This would be the transport itself (OlricAdapter, RedisAdapter)
type Adapter interface {
    GetItem(key string) (CacheItem, error)
    HasItem(key string) bool
    ForgetItem(key string) error // I like Forget better then Delete
    Save(CacheItem item) error
    /// Removes all items
    Clear() error
}

PSR-6 implements GetItems or SaveDefered but I don't think that we need these mechanism right away and would add them later on if needed. Thoughts?

Also must read on cache abstractions:

@dunglas
Copy link
Collaborator

dunglas commented Apr 1, 2021

It's a good start!

We also need to take into account the Vary header. To do so, we need a second level of cache. Several responses can be stored for a single tuple (method, URL). So we must first retrieve the Vary header form any entry matching this tuple in the cache, then compute the final cache key by adding to the method and the URL the normalized keys and values specified in Vary.

It's maybe doable with this interface, but maybe could we also do something taking this specific use case into account. AFIU Souin doesn't support Vary yet.

@soyuka
Copy link
Contributor

soyuka commented Apr 1, 2021

Can't Vary just be a part of the key? For example:

GET /book
Vary: User-Agent

Cache key: Headers['User-Agent'] . GET . /books

@dunglas
Copy link
Collaborator

dunglas commented Apr 1, 2021

No. Vary is typically a response header, not a request one.

@darkweak
Copy link
Collaborator

darkweak commented Apr 1, 2021

@darkweak
Copy link
Collaborator

darkweak commented Nov 6, 2024

Can we close that as we have the dynamic loading (thanks to the external storages) and each storages that implement the interface can be used as a cache-handler storage?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants