Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
64.91% covered (warning)
64.91%
37 / 57
66.67% covered (warning)
66.67%
4 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
CachedBagOStuff
66.07% covered (warning)
66.07%
37 / 56
66.67% covered (warning)
66.67%
4 / 6
55.47
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
3
 getMulti
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
20
 set
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 delete
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 add
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 merge
n/a
0 / 0
n/a
0 / 0
1
 changeTTL
n/a
0 / 0
n/a
0 / 0
1
 lock
n/a
0 / 0
n/a
0 / 0
1
 unlock
n/a
0 / 0
n/a
0 / 0
1
 deleteObjectsExpiringBefore
n/a
0 / 0
n/a
0 / 0
1
 setMulti
n/a
0 / 0
n/a
0 / 0
2
 deleteMulti
n/a
0 / 0
n/a
0 / 0
2
 changeTTLMulti
n/a
0 / 0
n/a
0 / 0
2
 incrWithInit
n/a
0 / 0
n/a
0 / 0
1
 setMockTime
n/a
0 / 0
n/a
0 / 0
1
1<?php
2/**
3 * Wrapper around a BagOStuff that caches data in memory
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup Cache
22 */
23
24namespace Wikimedia\ObjectCache;
25
26/**
27 * Wrapper around a BagOStuff that caches data in memory
28 *
29 * The differences between CachedBagOStuff and MultiWriteBagOStuff are:
30 * * CachedBagOStuff supports only one "backend".
31 * * There's a flag for writes to only go to the in-memory cache.
32 * * The in-memory cache is always updated.
33 * * Locks go to the backend cache (with MultiWriteBagOStuff, it would wind
34 *   up going to the HashBagOStuff used for the in-memory cache).
35 *
36 * @newable
37 * @ingroup Cache
38 */
39class CachedBagOStuff extends BagOStuff {
40    /** @var BagOStuff */
41    protected $store;
42    /** @var HashBagOStuff */
43    protected $procCache;
44
45    /**
46     * @stable to call
47     *
48     * @param BagOStuff $backend Permanent backend to use
49     * @param array $params Parameters for HashBagOStuff
50     */
51    public function __construct( BagOStuff $backend, $params = [] ) {
52        $params['keyspace'] = $backend->keyspace;
53        parent::__construct( $params );
54
55        $this->store = $backend;
56        $this->procCache = new HashBagOStuff( $params );
57
58        $this->attrMap = $backend->attrMap;
59    }
60
61    public function get( $key, $flags = 0 ) {
62        $value = $this->procCache->get( $key, $flags );
63        if ( $value !== false || $this->procCache->hasKey( $key ) ) {
64            return $value;
65        }
66
67        $value = $this->store->proxyCall(
68            __FUNCTION__,
69            self::ARG0_KEY,
70            self::RES_NONKEY,
71            func_get_args(),
72            $this
73        );
74        $this->set( $key, $value, self::TTL_INDEFINITE, self::WRITE_CACHE_ONLY );
75
76        return $value;
77    }
78
79    public function getMulti( array $keys, $flags = 0 ) {
80        $valueByKeyCached = [];
81
82        $keysFetch = [];
83        foreach ( $keys as $key ) {
84            $value = $this->procCache->get( $key, $flags );
85            if ( $value === false && !$this->procCache->hasKey( $key ) ) {
86                $keysFetch[] = $key;
87            } else {
88                $valueByKeyCached[$key] = $value;
89            }
90        }
91
92        $valueByKeyFetched = $this->store->proxyCall(
93            __FUNCTION__,
94            self::ARG0_KEYARR,
95            self::RES_KEYMAP,
96            [ $keysFetch, $flags ],
97            $this
98        );
99        $this->setMulti( $valueByKeyFetched, self::TTL_INDEFINITE, self::WRITE_CACHE_ONLY );
100
101        return $valueByKeyCached + $valueByKeyFetched;
102    }
103
104    public function set( $key, $value, $exptime = 0, $flags = 0 ) {
105        $this->procCache->set( $key, $value, $exptime, $flags );
106
107        if ( $this->fieldHasFlags( $flags, self::WRITE_CACHE_ONLY ) ) {
108            return true;
109        }
110
111        return $this->store->proxyCall(
112            __FUNCTION__,
113            self::ARG0_KEY,
114            self::RES_NONKEY,
115            func_get_args(),
116            $this
117        );
118    }
119
120    public function delete( $key, $flags = 0 ) {
121        $this->procCache->delete( $key, $flags );
122
123        if ( $this->fieldHasFlags( $flags, self::WRITE_CACHE_ONLY ) ) {
124            return true;
125        }
126
127        return $this->store->proxyCall(
128            __FUNCTION__,
129            self::ARG0_KEY,
130            self::RES_NONKEY,
131            func_get_args(),
132            $this
133        );
134    }
135
136    public function add( $key, $value, $exptime = 0, $flags = 0 ) {
137        if ( $this->get( $key ) === false ) {
138            return $this->set( $key, $value, $exptime, $flags );
139        }
140
141        // key already set
142        return false;
143    }
144
145    // These just call the backend (tested elsewhere)
146    // @codeCoverageIgnoreStart
147
148    public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
149        $this->procCache->delete( $key );
150
151        return $this->store->proxyCall(
152            __FUNCTION__,
153            self::ARG0_KEY,
154            self::RES_NONKEY,
155            func_get_args(),
156            $this
157        );
158    }
159
160    public function changeTTL( $key, $exptime = 0, $flags = 0 ) {
161        $this->procCache->delete( $key );
162
163        return $this->store->proxyCall(
164            __FUNCTION__,
165            self::ARG0_KEY,
166            self::RES_NONKEY,
167            func_get_args(),
168            $this
169        );
170    }
171
172    public function lock( $key, $timeout = 6, $exptime = 6, $rclass = '' ) {
173        return $this->store->proxyCall(
174            __FUNCTION__,
175            self::ARG0_KEY,
176            self::RES_NONKEY,
177            func_get_args(),
178            $this
179        );
180    }
181
182    public function unlock( $key ) {
183        return $this->store->proxyCall(
184            __FUNCTION__,
185            self::ARG0_KEY,
186            self::RES_NONKEY,
187            func_get_args(),
188            $this
189        );
190    }
191
192    public function deleteObjectsExpiringBefore(
193        $timestamp,
194        callable $progress = null,
195        $limit = INF,
196        string $tag = null
197    ) {
198        $this->procCache->deleteObjectsExpiringBefore( $timestamp, $progress, $limit, $tag );
199
200        return $this->store->proxyCall(
201            __FUNCTION__,
202            self::ARG0_NONKEY,
203            self::RES_NONKEY,
204            func_get_args(),
205            $this
206        );
207    }
208
209    public function setMulti( array $valueByKey, $exptime = 0, $flags = 0 ) {
210        $this->procCache->setMulti( $valueByKey, $exptime, $flags );
211
212        if ( $this->fieldHasFlags( $flags, self::WRITE_CACHE_ONLY ) ) {
213            return true;
214        }
215
216        return $this->store->proxyCall(
217            __FUNCTION__,
218            self::ARG0_KEYMAP,
219            self::RES_NONKEY,
220            func_get_args(),
221            $this
222        );
223    }
224
225    public function deleteMulti( array $keys, $flags = 0 ) {
226        $this->procCache->deleteMulti( $keys, $flags );
227
228        if ( $this->fieldHasFlags( $flags, self::WRITE_CACHE_ONLY ) ) {
229            return true;
230        }
231
232        return $this->store->proxyCall(
233            __FUNCTION__,
234            self::ARG0_KEYARR,
235            self::RES_NONKEY,
236            func_get_args(),
237            $this
238        );
239    }
240
241    public function changeTTLMulti( array $keys, $exptime, $flags = 0 ) {
242        $this->procCache->changeTTLMulti( $keys, $exptime, $flags );
243
244        if ( $this->fieldHasFlags( $flags, self::WRITE_CACHE_ONLY ) ) {
245            return true;
246        }
247
248        return $this->store->proxyCall(
249            __FUNCTION__,
250            self::ARG0_KEYARR,
251            self::RES_NONKEY,
252            func_get_args(),
253            $this
254        );
255    }
256
257    public function incrWithInit( $key, $exptime, $step = 1, $init = null, $flags = 0 ) {
258        $this->procCache->delete( $key );
259
260        return $this->store->proxyCall(
261            __FUNCTION__,
262            self::ARG0_KEY,
263            self::RES_NONKEY,
264            func_get_args(),
265            $this
266        );
267    }
268
269    public function setMockTime( &$time ) {
270        parent::setMockTime( $time );
271        $this->procCache->setMockTime( $time );
272        $this->store->setMockTime( $time );
273    }
274
275    // @codeCoverageIgnoreEnd
276}
277
278/** @deprecated class alias since 1.43 */
279class_alias( CachedBagOStuff::class, 'CachedBagOStuff' );