Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
66.07% covered (warning)
66.07%
37 / 56
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
24/**
25 * Wrapper around a BagOStuff that caches data in memory
26 *
27 * The differences between CachedBagOStuff and MultiWriteBagOStuff are:
28 * * CachedBagOStuff supports only one "backend".
29 * * There's a flag for writes to only go to the in-memory cache.
30 * * The in-memory cache is always updated.
31 * * Locks go to the backend cache (with MultiWriteBagOStuff, it would wind
32 *   up going to the HashBagOStuff used for the in-memory cache).
33 *
34 * @newable
35 * @ingroup Cache
36 */
37class CachedBagOStuff extends BagOStuff {
38    /** @var BagOStuff */
39    protected $store;
40    /** @var HashBagOStuff */
41    protected $procCache;
42
43    /**
44     * @stable to call
45     * @param BagOStuff $backend Permanent backend to use
46     * @param array $params Parameters for HashBagOStuff
47     */
48    public function __construct( BagOStuff $backend, $params = [] ) {
49        $params['keyspace'] = $backend->keyspace;
50        parent::__construct( $params );
51
52        $this->store = $backend;
53        $this->procCache = new HashBagOStuff( $params );
54
55        $this->attrMap = $backend->attrMap;
56    }
57
58    public function get( $key, $flags = 0 ) {
59        $value = $this->procCache->get( $key, $flags );
60        if ( $value !== false || $this->procCache->hasKey( $key ) ) {
61            return $value;
62        }
63
64        $value = $this->store->proxyCall(
65            __FUNCTION__,
66            self::ARG0_KEY,
67            self::RES_NONKEY,
68            func_get_args(),
69            $this
70        );
71        $this->set( $key, $value, self::TTL_INDEFINITE, self::WRITE_CACHE_ONLY );
72
73        return $value;
74    }
75
76    public function getMulti( array $keys, $flags = 0 ) {
77        $valueByKeyCached = [];
78
79        $keysFetch = [];
80        foreach ( $keys as $key ) {
81            $value = $this->procCache->get( $key, $flags );
82            if ( $value === false && !$this->procCache->hasKey( $key ) ) {
83                $keysFetch[] = $key;
84            } else {
85                $valueByKeyCached[$key] = $value;
86            }
87        }
88
89        $valueByKeyFetched = $this->store->proxyCall(
90            __FUNCTION__,
91            self::ARG0_KEYARR,
92            self::RES_KEYMAP,
93            [ $keysFetch, $flags ],
94            $this
95        );
96        $this->setMulti( $valueByKeyFetched, self::TTL_INDEFINITE, self::WRITE_CACHE_ONLY );
97
98        return $valueByKeyCached + $valueByKeyFetched;
99    }
100
101    public function set( $key, $value, $exptime = 0, $flags = 0 ) {
102        $this->procCache->set( $key, $value, $exptime, $flags );
103
104        if ( $this->fieldHasFlags( $flags, self::WRITE_CACHE_ONLY ) ) {
105            return true;
106        }
107
108        return $this->store->proxyCall(
109            __FUNCTION__,
110            self::ARG0_KEY,
111            self::RES_NONKEY,
112            func_get_args(),
113            $this
114        );
115    }
116
117    public function delete( $key, $flags = 0 ) {
118        $this->procCache->delete( $key, $flags );
119
120        if ( $this->fieldHasFlags( $flags, self::WRITE_CACHE_ONLY ) ) {
121            return true;
122        }
123
124        return $this->store->proxyCall(
125            __FUNCTION__,
126            self::ARG0_KEY,
127            self::RES_NONKEY,
128            func_get_args(),
129            $this
130        );
131    }
132
133    public function add( $key, $value, $exptime = 0, $flags = 0 ) {
134        if ( $this->get( $key ) === false ) {
135            return $this->set( $key, $value, $exptime, $flags );
136        }
137
138        // key already set
139        return false;
140    }
141
142    // These just call the backend (tested elsewhere)
143    // @codeCoverageIgnoreStart
144
145    public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
146        $this->procCache->delete( $key );
147
148        return $this->store->proxyCall(
149            __FUNCTION__,
150            self::ARG0_KEY,
151            self::RES_NONKEY,
152            func_get_args(),
153            $this
154        );
155    }
156
157    public function changeTTL( $key, $exptime = 0, $flags = 0 ) {
158        $this->procCache->delete( $key );
159
160        return $this->store->proxyCall(
161            __FUNCTION__,
162            self::ARG0_KEY,
163            self::RES_NONKEY,
164            func_get_args(),
165            $this
166        );
167    }
168
169    public function lock( $key, $timeout = 6, $exptime = 6, $rclass = '' ) {
170        return $this->store->proxyCall(
171            __FUNCTION__,
172            self::ARG0_KEY,
173            self::RES_NONKEY,
174            func_get_args(),
175            $this
176        );
177    }
178
179    public function unlock( $key ) {
180        return $this->store->proxyCall(
181            __FUNCTION__,
182            self::ARG0_KEY,
183            self::RES_NONKEY,
184            func_get_args(),
185            $this
186        );
187    }
188
189    public function deleteObjectsExpiringBefore(
190        $timestamp,
191        callable $progress = null,
192        $limit = INF,
193        string $tag = null
194    ) {
195        $this->procCache->deleteObjectsExpiringBefore( $timestamp, $progress, $limit, $tag );
196
197        return $this->store->proxyCall(
198            __FUNCTION__,
199            self::ARG0_NONKEY,
200            self::RES_NONKEY,
201            func_get_args(),
202            $this
203        );
204    }
205
206    public function setMulti( array $valueByKey, $exptime = 0, $flags = 0 ) {
207        $this->procCache->setMulti( $valueByKey, $exptime, $flags );
208
209        if ( $this->fieldHasFlags( $flags, self::WRITE_CACHE_ONLY ) ) {
210            return true;
211        }
212
213        return $this->store->proxyCall(
214            __FUNCTION__,
215            self::ARG0_KEYMAP,
216            self::RES_NONKEY,
217            func_get_args(),
218            $this
219        );
220    }
221
222    public function deleteMulti( array $keys, $flags = 0 ) {
223        $this->procCache->deleteMulti( $keys, $flags );
224
225        if ( $this->fieldHasFlags( $flags, self::WRITE_CACHE_ONLY ) ) {
226            return true;
227        }
228
229        return $this->store->proxyCall(
230            __FUNCTION__,
231            self::ARG0_KEYARR,
232            self::RES_NONKEY,
233            func_get_args(),
234            $this
235        );
236    }
237
238    public function changeTTLMulti( array $keys, $exptime, $flags = 0 ) {
239        $this->procCache->changeTTLMulti( $keys, $exptime, $flags );
240
241        if ( $this->fieldHasFlags( $flags, self::WRITE_CACHE_ONLY ) ) {
242            return true;
243        }
244
245        return $this->store->proxyCall(
246            __FUNCTION__,
247            self::ARG0_KEYARR,
248            self::RES_NONKEY,
249            func_get_args(),
250            $this
251        );
252    }
253
254    public function incrWithInit( $key, $exptime, $step = 1, $init = null, $flags = 0 ) {
255        $this->procCache->delete( $key );
256
257        return $this->store->proxyCall(
258            __FUNCTION__,
259            self::ARG0_KEY,
260            self::RES_NONKEY,
261            func_get_args(),
262            $this
263        );
264    }
265
266    public function setMockTime( &$time ) {
267        parent::setMockTime( $time );
268        $this->procCache->setMockTime( $time );
269        $this->store->setMockTime( $time );
270    }
271
272    // @codeCoverageIgnoreEnd
273}