Hi Sven, 2014-11-03 22:52 GMT+01:00 Sven Van Caekenberghe <sven@stfx.eu>:
Hi Thierry,
On 31 Oct 2014, at 14:10, Thierry Goubier <thierry.goubier@gmail.com> wrote:
Hi all,
I tried to use LRUCache for some application of mine with a two stages, over-the-internet data requests, and I couldn't find a way of using it.
My use case was: - retrieve some data with high latency (over 10 seconds) - in a interactive application, where I need to be able to continue while the data is being loaded.
So I designed it in two stages: - Have a cache - if the data isn't in the cache -- trigger a load with a fork -- put some temporary data in the cache (something which says loading) -- continue - When the data load is terminated -- replace the cached temporary data with the final version.
The thing is that I couldn't do the last one with the LRUCache (there is not at:put:, only a at:ifAbsentPut: []). Did I miss something?
Thierry
Relevant question and a rather special use case I would say.
The API of AbstractCache and its subclasses is minimal by design. It certainly does not contain everything that Dictionary has.
A cache delivers values based on keys. The ifAbsent: block is similar to the factory: block, it implements getting the value. Either you hit the cache or you miss it and it gets loaded. Entries drop off either because there are too many in some metric (LRUCache) or because they expire (TTLCache).
Now, #at:put: is like a replace, is it a hit or a miss ? Or neither ?
It would be a replace, and neither a hit nor a miss.
You could implement it with a #removeKey: and and #at:ifAbsent: - modulo some concurrent access problems.
I do those operations under a mutex, otherwise the system becomes incoherent :)
Maybe we should add it (I guess it could be useful to fill a cold cache as well) - but I am not totally convinced.
Now, your use case is a bit odd and dangerous. You spawn a thread/process for each miss, there could be a large number of them - do you want that ? Will the value always/still be needed when it arrives ?
A non needed value (to late value) will be discarded almost immediately, so this is not an issue. The number of threads may be high, yes, but it stays apparently within reasonable limits (how many Zn requests one can do simultaneously from Pharo? Is there any throttling?).
One way to implement your use case would be to encapsulate your special behaviour (the delayed loading) in the value itself (since you have the two cases/states exposed), no ?
Yes. This is more or less what I implemented, but without merging them in a single object. This makes using the LRUCache as is, but is no better than the previous approach about the number of threads in flight (those could be solved by a sharedQueue and a few loading processes feeding themselves out of the shared queue). Now I know how to use the LRUCache, but I think my use case is in a way too simple for its features (no real TTL, LRU only works if the cache size is over a certain threshold and that threshold isn't static). Thanks, Thierry
Sven