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