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

Api Method "deleteItemsByTagsAll()" removes unrelated items #713

Closed
di-rect opened this issue Nov 28, 2019 · 25 comments
Closed

Api Method "deleteItemsByTagsAll()" removes unrelated items #713

di-rect opened this issue Nov 28, 2019 · 25 comments

Comments

@di-rect
Copy link

di-rect commented Nov 28, 2019

When You add multiple items that have their own tag and also share a tag it seems that when you delete an item using deleteItemsByTagsAll(['sharedTag', 'ownTag']) that all items that share 'sharedTag' are also removed.

Following the docs it should only remove items that have multiple tags or all you set for this function, not one or more like deleteItemsByTags() does.

This happens for me on Couchbase buckets.

To Reproduce
Steps to reproduce the behavior:

  1. Save multiple items with a shared tag and their own tag (like ID)
  2. Remove one of the items by it's IDtag and the sharedTag using deleteItemsByTagsAll(['sharedTag', 'ownTag'])

So 10 items should be in total 21 => 10 items, 10 unique tags items, 1 shared tag item

Expected behavior
One item removed and one (unique) tag, so 2 items less in the bucket and the rest should stay

Current behavior
All items that share the shared tags are removed, so also are their individual tags.

@Geolim4
Copy link
Member

Geolim4 commented Nov 29, 2019

Hello,

I don't have much time actually, can you give me a sample of code so I can test it quickly ?

Cheers,
Georges

@di-rect
Copy link
Author

di-rect commented Nov 30, 2019

Hello,

I don't have much time actually, can you give me a sample of code so I can test it quickly ?

Cheers,
Georges

Hi,

Not by hand but I think your increment, or one of the other examples, fits. I see if I can make one out of it.

Regards,

@Geolim4
Copy link
Member

Geolim4 commented Dec 2, 2019

Hmmm I think I know why we have this kind of behavior:

Item A with tags "shared"
Item B with tags "shared, custom"

  • I check if the item's tags are all effectively in the deleteItemsByTagsAll(['shared')] request. Which is the case: A and B own this tag.

But B has also an additional tag: "custom" which is ignored, why ?
If the scenario was the following:

Item A with tags "shared, shared2"
Item B with tags "shared, custom"

If deleteItemsByTagsAll(['shared, shared2')] was called, only the item A would be deleted, because B doesn't have the tag "shared2"

In a nutshell: When deleteItemsByTagsAll() is called, I'm checking for the presence of all requested tags, but I'm not checking if all the tags in the ITEM are in the REQUEST.

This strange behavior will be fixed as soon I can.
Cheers,
Georges

@Geolim4
Copy link
Member

Geolim4 commented Dec 2, 2019

I think that all *ItemsByTagsAll() methods are affected by this, can you confirm this behavior to me @di-rect ?

@di-rect
Copy link
Author

di-rect commented Dec 2, 2019

@Geolim4 thanks for the feedback, sounds reasonable why it happened.

I cannot confirm if this happens with all *ItemsByTagsAll() I think I can test that for you but don't use them as I don't have a use-case form them in this stadium yet.

Have you seen the same behavior ?

@Geolim4
Copy link
Member

Geolim4 commented Dec 3, 2019

Have you seen the same behavior ?

I can't test it right now, this is the reason I'm asking you more details :)

@di-rect
Copy link
Author

di-rect commented Dec 3, 2019

Have you seen the same behavior ?

I can't test it right now, this is the reason I'm asking you more details :)

I need to see if I can make an in-code test-case. Otherwise I can try to make one but I found this out in the heat of the moment as well so I keep this open and I try to test it out as well.

If I'm right I need to create one, let me try to focus on that tomorrow.

@Geolim4 Geolim4 changed the title deleteItemsByTagsAll() removes unrelated items Api Method "deleteItemsByTagsAll()" removes unrelated items Dec 3, 2019
Geolim4 added a commit to Geolim4/phpfastcache that referenced this issue Dec 3, 2019
@Geolim4
Copy link
Member

Geolim4 commented Dec 3, 2019

In fact I'm realizing that by fixing this not "well-documented" behavior, I would introduce a massive breaking change which would go against my SEMVER commitment.

Instead I will add a new set of features in the next release of this summer called *ItemsByTagsAny()

Sorry, I can't fix this now, as it's more a not "well-documented" behavior rather than a real bug by itself.

The current behavior is:

  • Item1 has the following tags: shared, custom1
  • Item2 has the following tags: shared

Calling deleteItemsByTagsAll(['shared']); actually removes:

  • Item1
  • Item2

Why ? Because "Deleting item By All Requested Tags", here 'shared'.
Actually both items are matching this request, the behavior is indead unclear but working perfectly.
See tests/ItemTags.test.php which is testing deeply this behavior. (And fails completely if I'm fixing what you're describing:

No lets see what you describing/expecting:

The behavior would be:

  • Item1 has the following tags: shared, custom1
  • Item2 has the following tags: shared

Calling deleteItemsByTagsAll(['shared']); actually removes only item2, because:

  • Item1 has another tag not specified in deleteItemsByTagsAll call: custom1
  • Item2 is effectively removed because it has only one tag and this one is matching those passed to deleteItemsByTagsAll

So what now ?
Be patient for the V8 (around this summer), which will add *ItemsByTagsAny() support
or
Find another way to delete by tags :'(

@Geolim4 Geolim4 mentioned this issue Dec 3, 2019
6 tasks
@Geolim4
Copy link
Member

Geolim4 commented Dec 3, 2019

See here the list of broken tests:
https://travis-ci.org/PHPSocialNetwork/phpfastcache/jobs/620367464#L2381

I can not even "fix the tests" because this would implicitly shows that I'm introducing "incompatible breaking changes" in the middle of a minor/patch version, which is completely forbidden by SEMVER commitments.

Q: Why am I so compliant with Semver commitment ?
A: Because this is what describe the trust of developers to phpfastcache. If I'm introducing incompatible breaking changes in a "non-major" version I'm breaking the trust that users put on me.

@Geolim4
Copy link
Member

Geolim4 commented Dec 3, 2019

I'm not closing this bug and planned it as a feature for the V8 😄
Hope that you understand that decision.

Cheeres,
Georges

@di-rect
Copy link
Author

di-rect commented Dec 4, 2019

Hi,

Thank you for the explanation and work on this, I think we have some misunderstanding.

What I see is:

The current behavior is:

Item1 has the following tags: shared, custom1
Item2 has the following tags: shared
Calling deleteItemsByTagsAll(['shared','custom1']); actually removes:

Item1
Item2

=====================================

The behavior would be:

Item1 has the following tags: shared, custom1
Item2 has the following tags: shared
Calling deleteItemsByTagsAll(['shared', 'custom1']); actually removes only item1, because:

Item1 has both tags specified in deleteItemsByTagsAll call: shared, custom1
Item2 is not removed because it has only one tag and this one is not matching all those passed to deleteItemsByTagsAll

Item 2 would have been removed when it was called with deleteItemsByTags() as that does any match.

At some point I think I understand you but at the other side maybe you explain it wrong or we are both confused ?

I hope this helps, I think it's a false positive "bug".

Regards,

@Geolim4
Copy link
Member

Geolim4 commented Dec 4, 2019

The current behavior is:
Item1 has the following tags: shared, custom1
Item2 has the following tags: shared
Calling deleteItemsByTagsAll(['shared','custom1']); actually removes:
Item1
Item2

Nah, this behavior is fine, I tested it yesterday, and it's being already unit-tested, see by yourself the following code:

<?php

/**
 * @author Khoa Bui (khoaofgod)  <[email protected]> https://www.phpfastcache.com
 * @author Georges.L (Geolim4)  <[email protected]>
 */

use Phpfastcache\CacheManager;
use Phpfastcache\Drivers\Files\Config as FilesConfig;
use Phpfastcache\Helper\TestHelper;

chdir(__DIR__);
require_once __DIR__ . '/../../vendor/autoload.php';
$testHelper = new TestHelper('Github issue #713 - Api Method "deleteItemsByTagsAll()" removes unrelated items');
$cacheInstance = CacheManager::getInstance('Files', new FilesConfig(['securityKey' => 'test-713']));
$cacheInstance->clear();

$item1 = $cacheInstance->getItem('item1');
$item2 = $cacheInstance->getItem('item2');

$item1->addTags(['shared', 'custom1']);
$item1->set(1337);

$item2->addTags(['shared']);
$item2->set(1337);

$cacheInstance->saveMultiple($item1, $item2);
$cacheInstance->detachAllItems();
unset($item1, $item2);
$cacheInstance->deleteItemsByTagsAll(['shared', 'custom1']);

$item1 = $cacheInstance->getItem('item1');
$item2 = $cacheInstance->getItem('item2');

var_dump($item1->isHit());
var_dump($item2->isHit());

if ($item1->isHit()) {
    $testHelper->printPassText('Item #1 is still in cache as expected.');
} else {
    $testHelper->printFailText('Item #1 is no longer in cache and so has been unexpectedly removed.');
}

$testHelper->terminateTest();

Current Output:

[PHPFASTCACHE CORE V7.1.0#11C7B17]
[PHPFASTCACHE API V2.0.4]
[PHP V7.2.5]
[Begin Test: 'Github issue #713 - Api Method "deleteItemsByTagsAll()" removes unrelated items']
---
bool(false)
bool(true)
[FAIL] Item #1 is no longer in cache and so has been unexpectedly removed.
Test duration: 0.134s

Process finished with exit code 1

As you can see, item1 has been removed while item2 still exists.

@Geolim4
Copy link
Member

Geolim4 commented Dec 4, 2019

(This is the test I had prepared before I realized that I would introduce a massive breaking change).

@di-rect
Copy link
Author

di-rect commented Dec 5, 2019

The current behavior is:
... a lot of code ...
As you can see, item1 has been removed while item2 still exists.

In the behavior here I see all items going down to zero in my cache, which is strange. I need to investigate what goes wrong (here).

It is still a good topic to discuss :)

@wolfgangvc
Copy link

wolfgangvc commented Dec 17, 2019

I think it might first be useful to have a better description of what the functions are supposed to do.

The current documentation is a bit vague and awkwardly worded

deleteItemsByTag - Removes the item from the pool by tag.
deleteItemsByTags - Removes the item from the pool by one of multiple tag names.
deleteItemsByTagsAll - Removes the item from the pool by all of multiple tag names.

As I understand their functionality they would be much better described with the following

deleteItemsByTag - Removes items from the pool that have the given tag.
deleteItemsByTags - Removes items from the pool that have at least one of the given tags.
deleteItemsByTagsAll - Removes items from the pool that have all of the given tags.

Making it clear that deleteItemsByTags is functionally the same as

foreach($tags as $tag){
    $pool->deleteItemsByTag($tag)
}

And that deleteItemsByTagsAll is functionally the same as

foreach($pool->getItems() as $item){
    foreach($tags as $tag){
        if( !in_array($tag, $items->getTags() ){
            continue 2;
        }
        $pool->deleteItem($item->getKey());
    }
}

Atleast with my testing this seems to be the case.  

@Geolim4
Copy link
Member

Geolim4 commented Dec 22, 2019

@wolfgangvc The README already describes this behavior but maybe not enough.

Geolim4 added a commit that referenced this issue Jan 2, 2020
@Geolim4
Copy link
Member

Geolim4 commented Jan 2, 2020

Hello !!

🆕 in V8: Multiple strategies ($strategy) are now supported for tagging:

  • TaggableCacheItemPoolInterface::TAG_STRATEGY_ONE allows you to get cache item(s) by at least ONE of the specified matching tag(s). Default behavior.
  • TaggableCacheItemPoolInterface::TAG_STRATEGY_ALL allows you to get cache item(s) by ALL of the specified matching tag(s) (the cache item can have additional tag(s))
  • TaggableCacheItemPoolInterface::TAG_STRATEGY_ONLY allows you to get cache item(s) by ONLY the specified matching tag(s) (the cache item cannot have additional tag(s))

This has been implemented as of the API v3

  • [BC] Removed ExtendedCacheItemPoolInterface::appendItemsByTagsAll() (replaced by strategy TaggableCacheItemPoolInterface::TAG_STRATEGY_ALL)
  • [BC] Removed ExtendedCacheItemPoolInterface::decrementItemsByTagsAll() (replaced by strategy TaggableCacheItemPoolInterface::TAG_STRATEGY_ALL)
  • [BC] Removed ExtendedCacheItemPoolInterface::deleteItemsByTagsAll() (replaced by strategy TaggableCacheItemPoolInterface::TAG_STRATEGY_ALL)
  • [BC] Removed ExtendedCacheItemPoolInterface::getItemsByTagsAll() (replaced by strategy TaggableCacheItemPoolInterface::TAG_STRATEGY_ALL)
  • [BC] Removed ExtendedCacheItemPoolInterface::incrementItemsByTagsAll() (replaced by strategy TaggableCacheItemPoolInterface::TAG_STRATEGY_ALL)
  • [BC] Removed ExtendedCacheItemPoolInterface::prependItemsByTagsAll() (replaced by strategy TaggableCacheItemPoolInterface::TAG_STRATEGY_ALL)
  • Added strategyTaggableCacheItemPoolInterface::TAG_STRATEGY_ONE usable in every **byTags** methods.
  • Added strategyTaggableCacheItemPoolInterface::TAG_STRATEGY_ALL usable in every **byTags** methods.
  • Added strategyTaggableCacheItemPoolInterface::TAG_STRATEGY_ONLY usable in every **byTags** methods.

Expect the V8 to be released by the summer 2020 :)

Cheers,
Georges

@Geolim4 Geolim4 closed this as completed Jan 2, 2020
@di-rect
Copy link
Author

di-rect commented Jan 30, 2020

Sorry for my late response, but this is great!

Is V8 already usable without being released ?

Thanks a lot!

@Geolim4
Copy link
Member

Geolim4 commented Jan 30, 2020

Sorry for my late response, but this is great!

Is V8 already usable without being released ?

Thanks a lot!

Hello, nope it's still in development status, despite the tests are ok, I would not recommend it for use in a production environment !

I still have a bunch of docs & tests to made.

@di-rect
Copy link
Author

di-rect commented Feb 5, 2020

Hi,

I got mentioned by someone (that was involved here), maybe this is usable for this as well to make dev easier ? https://github.com/team-a-pro/collection

@Geolim4
Copy link
Member

Geolim4 commented Feb 5, 2020

See the discussion above :)

@di-rect
Copy link
Author

di-rect commented Feb 5, 2020

See the discussion above :)

I know but I meant if future development makes it easier why not switch instead ? Not that it's needed, if it saves time I would say... a refactor is never a bad learning curve :)

@Geolim4
Copy link
Member

Geolim4 commented Feb 5, 2020

Yes it does what you meant, because the tags now supports all possible use cases. It is implemented in V8 because it's not backward compatible and would break the SemVer.

@Geolim4
Copy link
Member

Geolim4 commented Feb 5, 2020

@di-rect
Copy link
Author

di-rect commented Feb 6, 2020

Which is great! I though I just notice you when you want to be 'lazy' in the future :)

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

No branches or pull requests

3 participants