renoki-co/laravel-eloquent-query-cache

On update, only flush the updated model from the cache - not all of them

leenooks opened this issue · 4 comments

Hi,

Is it possible to just flush the "updated" model from the cache, instead of all models (of the same type)?

IE: If I have a model "Foo" and I query from the DB id 1 and id 2, then a subsequent query for id 1 or id 2 should result in the data come from the cache. However, if I update id 1, and have have $flushCacheOnUpdate=TRUE set, then both id 1 and id 2 are flushed from the cache.

I would like just id 1 to be flushed, so that the next query for that ID retrieves the data from the DB (with the updates).

Setting $flushCacheOnUpdate will invalidate the entire caching. Instead, you might want to do a manual flush.

  1. You might do manual invalidation:
$user1 = User::cacheFor(60)->cacheTags(['id:1'])->find(1);
$user2 = User::cacheFor(60)->cacheTags(['id:2'])->find(2);

$user1->update(['name' => 'John']);

User::flushQueryCache(['id:1']);
  1. If you still want to use $flushCacheOnUpdate = true;, update the tags that will get flushed upon updates:
class User {
    /**
     * Invalidate the cache automatically
     * upon update in the database.
     *
     * @var bool
     */
    protected static $flushCacheOnUpdate = true;

    /**
     * Set the base cache tags that will be present
     * on all queries.
     *
     * @return array
     */
    protected function getCacheBaseTags(): array
    {
        return [
            "id:{$this->id}",
        ];
    }
}

$user1 = User::cacheFor(60)->cacheTags(['id:1'])->find(1);
$user2 = User::cacheFor(60)->cacheTags(['id:2'])->find(2);

$user1->update(['name' => 'John']);

// Cache flushed successfully only for user 1

So I've been trying the second method - but I'm not using the ->cacheTags() in the query.

IE: If I have

$o = Import::find($this->argument('arg'));
$o->active = ! $o->active;
$o->save();
$this->info('Changed active to :'.($o->active ? 'ON' : 'OFF'));
dump(['now'=>Import::find($this->argument('arg'))->active ? 'ON' : 'OFF']);

I only ever get:

Changed active to :ON
array:1 [
  "now" => "OFF"
]

So the only way to achieve it is if I have cacheTags() in the original query? (IE: It cannot automatically do it just using getCacheBaseTags()) ?

This package caches the queries, not the models.

I'm not sure I follow - but...

One of the attractive attributes of this library is that caching is added with minimal change to existing code. (Hence why I dont use cacheTags() yet in my queries.)

So I tried this:
getCacheBaseTags() on the Model is as you have it.

Then,

$cachetag = sprintf('%s:%d','id',$this->argument('arg'));
$o = Import::cacheTag($cachetag)->find($this->argument('arg'));
$o->active = ! $o->active;
$o->save();
$this->info('Changed active to :'.($o->active ? 'ON' : 'OFF'));
dump(['now'=>Import::find($this->argument('arg'))->active ? 'ON' : 'OFF']);

(Notice in the final query I have not added the cacheTag(). So while the DB is updated correctly, any subsequent queries not using the cacheTag() id yields a static stale record :( )

I think the answer is yes - but I have to use cacheTag() consistently if I want to expire only changed records from the cache (of the same model), and still have my queries return the correct (cached) record?

(I do a large import of 10,000's records of multiple models - and the DB is remote, so I was planning on using a local redis cache to reduce some of the latency to the DB, where I do some queries to validate some records before I create/update a record. So during the import, I dont want to expire unchanged records.)