Skip to content

[Cache] Respect $save option in all adapters #46699

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

Merged
merged 2 commits into from
Jun 19, 2022

Conversation

jrjohnson
Copy link
Contributor

Q A
Branch? 4.4
Bug fix? yes
New feature? no
Deprecations? no
Tickets
License MIT
Doc PR

I was working with a cache chain which included the array adapter. When we have a cache miss we need to consolidate all of the misses so we can do a single DB query for all values. So first we look in the cache, then and then we can pull the data as a big group, hydrate the results, and then cache them. Because we search for these values a bunch of times in the same request I chained the array and Redis adapter together.

Looks like:

$hitsAndMisses = array_map(
  fn(mixed $id) => $this->cache->get($id, function (ItemInterface $item, bool &$save) {
    $save = false;
    return false;
  }),
  $ids
);

 $hits = array_filter($hitsAndMisses);

//cut: compare arrays to get missed Ids

$hydrated = $this->hydrate($missedIds); //very expensive to do individually

$missedValues = array_map(fn($obj) => $this->cache->get(
  $obj->id,
  function (ItemInterface $item) use ($obj) {
    return $obj;
  }
), $hydrated);

return array_values([...$hits, ...$missedValues]);

Unfortunately neither the ArrayAdapter nor the ChainAdapter respect the $save reference. I was able to fix the ArrayAdapter in this PR, but I'm at a loss for how to do the same for the ChainAdapter, but I was able to create a generic failing test.

When $save is passed as the second option to the callback it should be
respected, even in the ephemeral array adapter.
@carsonbot
Copy link

Hey!

I think @sbelyshkin has recently worked with this code. Maybe they can help review this?

Cheers!

Carsonbot

@sbelyshkin
Copy link
Contributor

@jrjohnson I don't know all the details of your case but from my perspective PSR-6 methods is the best choice for your scenario. RedisAdapter::getItems() can fetch all the objects with a single MGET, and then commit() will send all saveDeffered() SET operations in a pipeline. In this way you can reduce the round trip time.

$hitsAndMisses = $this->cache->getItems($ids); // produces MGET id1, id2,... for Redis

$hits = $misses = [];
foreach($hitsAndMisses as $cacheItem) {
    if ($cacheItem->isHit()) {
       $hits[$cacheItem->getKey()] = $cacheItem->get();
    } else {
       $misses[$cacheItem->getKey()] = $cacheItem;
    }
}

$hydrated = $this->hydrate(array_keys($misses)); //very expensive to do individually

foreach($hydrated as $obj) {
    $this->cache->saveDeffered($misses[$obj->id]->set($obj));
}

$this->cache->commit(); // sends all SET commands in a pipeline and reads a single response

return array_values([...$hits, ...$hydrated]);

get() is obviously designed for dealing with single items.
At the same time, I agree that the described behavior of ArrayAdapter looks unexpected, compared to the others.

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch @jrjohnson
I just added the commit to fix ChainAdapter.

@nicolas-grekas
Copy link
Member

Thank you @jrjohnson.

@nicolas-grekas nicolas-grekas merged commit fa4a7a4 into symfony:4.4 Jun 19, 2022
@jrjohnson
Copy link
Contributor Author

Happy to help!

Thanks @sbelyshkin, for the info. I need to take advantage of tags and I don't think I can do that with the PSR6 cache. I'm going to refactor our reads to use those methods though and save some significant back and forth!

@jrjohnson jrjohnson deleted the array-adapter-save branch June 22, 2022 06:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants