2014-08-16 00:26:42 +00:00
|
|
|
<?php
|
|
|
|
|
2022-11-02 20:14:24 +00:00
|
|
|
namespace MediaWiki\Extension\Notifications\Cache;
|
|
|
|
|
|
|
|
use Iterator;
|
|
|
|
use MapCacheLRU;
|
|
|
|
|
2014-08-16 00:26:42 +00:00
|
|
|
/**
|
|
|
|
* Base Local cache object, which borrows the concept from Flow user listener
|
|
|
|
*/
|
2022-11-02 20:14:24 +00:00
|
|
|
abstract class LocalCache {
|
2014-08-16 00:26:42 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Max number of objects to hold in $targets. In theory, 1000
|
|
|
|
* is very hard to reach in a normal web request. We need to
|
2022-11-02 20:14:24 +00:00
|
|
|
* put cap, so it doesn't reach memory limit when running email
|
2022-06-23 09:17:10 +00:00
|
|
|
* digest against large amount of notifications
|
2014-08-16 00:26:42 +00:00
|
|
|
*/
|
2020-09-19 18:55:32 +00:00
|
|
|
private const TARGET_MAX_NUM = 1000;
|
2014-08-16 00:26:42 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Target object cache
|
2018-08-15 19:30:45 +00:00
|
|
|
* @var MapCacheLRU
|
2014-08-16 00:26:42 +00:00
|
|
|
*/
|
|
|
|
protected $targets;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Lookup ids that have not been resolved for a target
|
2019-12-21 05:45:14 +00:00
|
|
|
* @var bool[]
|
2014-08-16 00:26:42 +00:00
|
|
|
*/
|
cache: Reduce responsibility of subclass and remove unused parts
* Reduce responsibility of resolve() methods to only supplying
the resolves values.
Moved logic for populating the cache and clearing the queue
to the base class, and made 'lookups' private.
* The second parameter to LocalCache::add() is unused, and never passed.
Removed to avoid confusion.
* The getTargets() method is unused. Removed.
* The getLookups() method is unused. Removed.
* The internal 'lookups' member was being used both for its keys and its
values, but never at the same time. This seemed risky, especially in
EchoRevisionLocalCache::resolve() where the associative array was passed
directly to the 'where' clause of IDatabase::select(), which shouldn't
espect keys when creating the 'IN' clause.
Using only values would keep value types flexible, but would require
use of the less efficient in_array().
Keeping both keys and values and calling array_values() would work.
Using only keys also works and is simpler, so long only ints are used.
* The tests were swapping 'targets' MapCacheLRU with a HashBagOStuff.
Following-up 4939bff7, this was forgotten, but works because the two
called methods (get and set) exist in both, but still seems odd.
Fixed by using TestingAccessWrapper to act on the existing object
instead of swapping it out.
* Improved tests by asserting more of the observed behaviour and impact.
Change-Id: I530eeac8bf3b407b8c633e0e20c7d35cc49f7a9f
2018-08-17 01:45:57 +00:00
|
|
|
private $lookups = [];
|
2014-08-16 00:26:42 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Resolve ids in lookups to targets
|
cache: Reduce responsibility of subclass and remove unused parts
* Reduce responsibility of resolve() methods to only supplying
the resolves values.
Moved logic for populating the cache and clearing the queue
to the base class, and made 'lookups' private.
* The second parameter to LocalCache::add() is unused, and never passed.
Removed to avoid confusion.
* The getTargets() method is unused. Removed.
* The getLookups() method is unused. Removed.
* The internal 'lookups' member was being used both for its keys and its
values, but never at the same time. This seemed risky, especially in
EchoRevisionLocalCache::resolve() where the associative array was passed
directly to the 'where' clause of IDatabase::select(), which shouldn't
espect keys when creating the 'IN' clause.
Using only values would keep value types flexible, but would require
use of the less efficient in_array().
Keeping both keys and values and calling array_values() would work.
Using only keys also works and is simpler, so long only ints are used.
* The tests were swapping 'targets' MapCacheLRU with a HashBagOStuff.
Following-up 4939bff7, this was forgotten, but works because the two
called methods (get and set) exist in both, but still seems odd.
Fixed by using TestingAccessWrapper to act on the existing object
instead of swapping it out.
* Improved tests by asserting more of the observed behaviour and impact.
Change-Id: I530eeac8bf3b407b8c633e0e20c7d35cc49f7a9f
2018-08-17 01:45:57 +00:00
|
|
|
*
|
|
|
|
* @param int[] $lookups
|
|
|
|
* @return Iterator
|
2014-08-16 00:26:42 +00:00
|
|
|
*/
|
cache: Reduce responsibility of subclass and remove unused parts
* Reduce responsibility of resolve() methods to only supplying
the resolves values.
Moved logic for populating the cache and clearing the queue
to the base class, and made 'lookups' private.
* The second parameter to LocalCache::add() is unused, and never passed.
Removed to avoid confusion.
* The getTargets() method is unused. Removed.
* The getLookups() method is unused. Removed.
* The internal 'lookups' member was being used both for its keys and its
values, but never at the same time. This seemed risky, especially in
EchoRevisionLocalCache::resolve() where the associative array was passed
directly to the 'where' clause of IDatabase::select(), which shouldn't
espect keys when creating the 'IN' clause.
Using only values would keep value types flexible, but would require
use of the less efficient in_array().
Keeping both keys and values and calling array_values() would work.
Using only keys also works and is simpler, so long only ints are used.
* The tests were swapping 'targets' MapCacheLRU with a HashBagOStuff.
Following-up 4939bff7, this was forgotten, but works because the two
called methods (get and set) exist in both, but still seems odd.
Fixed by using TestingAccessWrapper to act on the existing object
instead of swapping it out.
* Improved tests by asserting more of the observed behaviour and impact.
Change-Id: I530eeac8bf3b407b8c633e0e20c7d35cc49f7a9f
2018-08-17 01:45:57 +00:00
|
|
|
abstract protected function resolve( array $lookups );
|
2014-08-16 00:26:42 +00:00
|
|
|
|
|
|
|
/**
|
2023-08-14 15:36:07 +00:00
|
|
|
* Instances should be obtained via EchoServices / MediaWikiServices.
|
cache: Reduce responsibility of subclass and remove unused parts
* Reduce responsibility of resolve() methods to only supplying
the resolves values.
Moved logic for populating the cache and clearing the queue
to the base class, and made 'lookups' private.
* The second parameter to LocalCache::add() is unused, and never passed.
Removed to avoid confusion.
* The getTargets() method is unused. Removed.
* The getLookups() method is unused. Removed.
* The internal 'lookups' member was being used both for its keys and its
values, but never at the same time. This seemed risky, especially in
EchoRevisionLocalCache::resolve() where the associative array was passed
directly to the 'where' clause of IDatabase::select(), which shouldn't
espect keys when creating the 'IN' clause.
Using only values would keep value types flexible, but would require
use of the less efficient in_array().
Keeping both keys and values and calling array_values() would work.
Using only keys also works and is simpler, so long only ints are used.
* The tests were swapping 'targets' MapCacheLRU with a HashBagOStuff.
Following-up 4939bff7, this was forgotten, but works because the two
called methods (get and set) exist in both, but still seems odd.
Fixed by using TestingAccessWrapper to act on the existing object
instead of swapping it out.
* Improved tests by asserting more of the observed behaviour and impact.
Change-Id: I530eeac8bf3b407b8c633e0e20c7d35cc49f7a9f
2018-08-17 01:45:57 +00:00
|
|
|
*
|
|
|
|
* @private
|
2014-08-16 00:26:42 +00:00
|
|
|
*/
|
cache: Reduce responsibility of subclass and remove unused parts
* Reduce responsibility of resolve() methods to only supplying
the resolves values.
Moved logic for populating the cache and clearing the queue
to the base class, and made 'lookups' private.
* The second parameter to LocalCache::add() is unused, and never passed.
Removed to avoid confusion.
* The getTargets() method is unused. Removed.
* The getLookups() method is unused. Removed.
* The internal 'lookups' member was being used both for its keys and its
values, but never at the same time. This seemed risky, especially in
EchoRevisionLocalCache::resolve() where the associative array was passed
directly to the 'where' clause of IDatabase::select(), which shouldn't
espect keys when creating the 'IN' clause.
Using only values would keep value types flexible, but would require
use of the less efficient in_array().
Keeping both keys and values and calling array_values() would work.
Using only keys also works and is simpler, so long only ints are used.
* The tests were swapping 'targets' MapCacheLRU with a HashBagOStuff.
Following-up 4939bff7, this was forgotten, but works because the two
called methods (get and set) exist in both, but still seems odd.
Fixed by using TestingAccessWrapper to act on the existing object
instead of swapping it out.
* Improved tests by asserting more of the observed behaviour and impact.
Change-Id: I530eeac8bf3b407b8c633e0e20c7d35cc49f7a9f
2018-08-17 01:45:57 +00:00
|
|
|
public function __construct() {
|
2018-08-15 19:30:45 +00:00
|
|
|
$this->targets = new MapCacheLRU( self::TARGET_MAX_NUM );
|
2014-08-16 00:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a key to the lookup and the key is used to resolve cache target
|
|
|
|
*
|
|
|
|
* @param int $key
|
|
|
|
*/
|
cache: Reduce responsibility of subclass and remove unused parts
* Reduce responsibility of resolve() methods to only supplying
the resolves values.
Moved logic for populating the cache and clearing the queue
to the base class, and made 'lookups' private.
* The second parameter to LocalCache::add() is unused, and never passed.
Removed to avoid confusion.
* The getTargets() method is unused. Removed.
* The getLookups() method is unused. Removed.
* The internal 'lookups' member was being used both for its keys and its
values, but never at the same time. This seemed risky, especially in
EchoRevisionLocalCache::resolve() where the associative array was passed
directly to the 'where' clause of IDatabase::select(), which shouldn't
espect keys when creating the 'IN' clause.
Using only values would keep value types flexible, but would require
use of the less efficient in_array().
Keeping both keys and values and calling array_values() would work.
Using only keys also works and is simpler, so long only ints are used.
* The tests were swapping 'targets' MapCacheLRU with a HashBagOStuff.
Following-up 4939bff7, this was forgotten, but works because the two
called methods (get and set) exist in both, but still seems odd.
Fixed by using TestingAccessWrapper to act on the existing object
instead of swapping it out.
* Improved tests by asserting more of the observed behaviour and impact.
Change-Id: I530eeac8bf3b407b8c633e0e20c7d35cc49f7a9f
2018-08-17 01:45:57 +00:00
|
|
|
public function add( $key ) {
|
2014-08-16 00:26:42 +00:00
|
|
|
if (
|
|
|
|
count( $this->lookups ) < self::TARGET_MAX_NUM
|
2019-12-21 05:45:14 +00:00
|
|
|
&& !$this->targets->get( (string)$key )
|
2014-08-16 00:26:42 +00:00
|
|
|
) {
|
cache: Reduce responsibility of subclass and remove unused parts
* Reduce responsibility of resolve() methods to only supplying
the resolves values.
Moved logic for populating the cache and clearing the queue
to the base class, and made 'lookups' private.
* The second parameter to LocalCache::add() is unused, and never passed.
Removed to avoid confusion.
* The getTargets() method is unused. Removed.
* The getLookups() method is unused. Removed.
* The internal 'lookups' member was being used both for its keys and its
values, but never at the same time. This seemed risky, especially in
EchoRevisionLocalCache::resolve() where the associative array was passed
directly to the 'where' clause of IDatabase::select(), which shouldn't
espect keys when creating the 'IN' clause.
Using only values would keep value types flexible, but would require
use of the less efficient in_array().
Keeping both keys and values and calling array_values() would work.
Using only keys also works and is simpler, so long only ints are used.
* The tests were swapping 'targets' MapCacheLRU with a HashBagOStuff.
Following-up 4939bff7, this was forgotten, but works because the two
called methods (get and set) exist in both, but still seems odd.
Fixed by using TestingAccessWrapper to act on the existing object
instead of swapping it out.
* Improved tests by asserting more of the observed behaviour and impact.
Change-Id: I530eeac8bf3b407b8c633e0e20c7d35cc49f7a9f
2018-08-17 01:45:57 +00:00
|
|
|
$this->lookups[$key] = true;
|
2014-08-16 00:26:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the cache target based on the key
|
|
|
|
*
|
|
|
|
* @param int $key
|
|
|
|
* @return mixed|null
|
|
|
|
*/
|
|
|
|
public function get( $key ) {
|
2019-12-21 05:45:14 +00:00
|
|
|
$target = $this->targets->get( (string)$key );
|
2014-08-16 00:26:42 +00:00
|
|
|
if ( $target ) {
|
|
|
|
return $target;
|
|
|
|
}
|
|
|
|
|
cache: Reduce responsibility of subclass and remove unused parts
* Reduce responsibility of resolve() methods to only supplying
the resolves values.
Moved logic for populating the cache and clearing the queue
to the base class, and made 'lookups' private.
* The second parameter to LocalCache::add() is unused, and never passed.
Removed to avoid confusion.
* The getTargets() method is unused. Removed.
* The getLookups() method is unused. Removed.
* The internal 'lookups' member was being used both for its keys and its
values, but never at the same time. This seemed risky, especially in
EchoRevisionLocalCache::resolve() where the associative array was passed
directly to the 'where' clause of IDatabase::select(), which shouldn't
espect keys when creating the 'IN' clause.
Using only values would keep value types flexible, but would require
use of the less efficient in_array().
Keeping both keys and values and calling array_values() would work.
Using only keys also works and is simpler, so long only ints are used.
* The tests were swapping 'targets' MapCacheLRU with a HashBagOStuff.
Following-up 4939bff7, this was forgotten, but works because the two
called methods (get and set) exist in both, but still seems odd.
Fixed by using TestingAccessWrapper to act on the existing object
instead of swapping it out.
* Improved tests by asserting more of the observed behaviour and impact.
Change-Id: I530eeac8bf3b407b8c633e0e20c7d35cc49f7a9f
2018-08-17 01:45:57 +00:00
|
|
|
if ( isset( $this->lookups[ $key ] ) ) {
|
|
|
|
// Resolve the lookup batch and store results in the cache
|
|
|
|
$targets = $this->resolve( array_keys( $this->lookups ) );
|
|
|
|
foreach ( $targets as $id => $val ) {
|
|
|
|
$this->targets->set( $id, $val );
|
|
|
|
}
|
|
|
|
$this->lookups = [];
|
2019-12-21 05:45:14 +00:00
|
|
|
$target = $this->targets->get( (string)$key );
|
2014-08-16 00:26:42 +00:00
|
|
|
if ( $target ) {
|
|
|
|
return $target;
|
|
|
|
}
|
|
|
|
}
|
2015-10-01 13:48:52 +00:00
|
|
|
|
2014-08-16 00:26:42 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clear everything in local cache
|
|
|
|
*/
|
|
|
|
public function clearAll() {
|
|
|
|
$this->targets->clear();
|
2016-12-05 18:51:07 +00:00
|
|
|
$this->lookups = [];
|
2014-08-16 00:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|