1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SMW\Query\Result; |
4
|
|
|
|
5
|
|
|
use Onoi\BlobStore\BlobStore; |
6
|
|
|
use SMWQuery as Query; |
7
|
|
|
use SMWQueryResult as QueryResult; |
8
|
|
|
use SMW\Store; |
9
|
|
|
use SMW\DIWikiPage; |
10
|
|
|
use SMW\QueryEngine; |
11
|
|
|
use SMW\QueryFactory; |
12
|
|
|
use Psr\Log\LoggerInterface; |
13
|
|
|
use Psr\Log\LoggerAwareInterface; |
14
|
|
|
use SMW\Utils\BufferedStatsdCollector; |
15
|
|
|
use SMW\Utils\Timer; |
16
|
|
|
use SMW\ApplicationFactory; |
17
|
|
|
use RuntimeException; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* The prefetcher only caches the subject list from a computed a query |
21
|
|
|
* condition. The result is processed before an individual query printer has |
22
|
|
|
* access to the query result hence it does not interfere with the final string |
23
|
|
|
* output manipulation. |
24
|
|
|
* |
25
|
|
|
* The main objective is to avoid unnecessary computing of results for queries |
26
|
|
|
* that have the same query signature. PrintRequests as part of a QueryResult |
27
|
|
|
* object are not cached and are not part of a query signature. |
28
|
|
|
* |
29
|
|
|
* Cache eviction is carried out either manually (action=purge) or executed |
30
|
|
|
* through the QueryDepedencyLinksStore. |
31
|
|
|
* |
32
|
|
|
* @license GNU GPL v2+ |
33
|
|
|
* @since 2.5 |
34
|
|
|
* |
35
|
|
|
* @author mwjames |
36
|
|
|
*/ |
37
|
|
|
class CachedQueryResultPrefetcher implements QueryEngine, LoggerAwareInterface { |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Update this version number when the serialization format |
41
|
|
|
* changes. |
42
|
|
|
*/ |
43
|
|
|
const VERSION = '1'; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Namespace occupied by the BlobStore |
47
|
|
|
*/ |
48
|
|
|
const CACHE_NAMESPACE = 'smw:query:store'; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* ID used by the bufferedStatsdCollector, requires to be changed in case |
52
|
|
|
* the data schema is modified |
53
|
|
|
* |
54
|
|
|
* PHP 5.6 can do self::CACHE_NAMESPACE . ':' . self::VERSION |
55
|
|
|
*/ |
56
|
|
|
const STATSD_ID = 'smw:query:store:1:d:'; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* ID for the tempCache |
60
|
|
|
*/ |
61
|
|
|
const POOLCACHE_ID = 'queryresult.prefetcher'; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* @var Store |
65
|
|
|
*/ |
66
|
|
|
private $store; |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @var QueryFactory |
70
|
|
|
*/ |
71
|
|
|
private $queryFactory; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @var BlobStore |
75
|
|
|
*/ |
76
|
|
|
private $blobStore; |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @var QueryEngine |
80
|
|
|
*/ |
81
|
|
|
private $queryEngine; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @var BufferedStatsdCollector |
85
|
|
|
*/ |
86
|
|
|
private $bufferedStatsdCollector; |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* @var integer|boolean |
90
|
|
|
*/ |
91
|
|
|
private $nonEmbeddedCacheLifetime = false; |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* @var boolean |
95
|
|
|
*/ |
96
|
|
|
private $enabledCache = true; |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @var loggerInterface |
100
|
|
|
*/ |
101
|
|
|
private $logger; |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Keep a temp cache to hold on query results that aren't stored yet. |
105
|
|
|
* |
106
|
|
|
* If for example the retrieval is executed in deferred mode then a request |
107
|
|
|
* may occur in the same transaction cycle without being stored to the actual |
108
|
|
|
* back-end, yet queries with the same signature may have been retrieved |
109
|
|
|
* already therefore allow to recall the result from tempCache. |
110
|
|
|
* |
111
|
|
|
* @var InMemoryCache |
112
|
|
|
*/ |
113
|
|
|
private $tempCache; |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* An internal change to the query execution may occur without being detected |
117
|
|
|
* by the Description hash (which is the desired behaviour) and to avoid a |
118
|
|
|
* stalled cache on an altered execution plan, use this modifier to generate |
119
|
|
|
* a new hash. |
120
|
|
|
* |
121
|
|
|
* @var string/integer |
122
|
|
|
*/ |
123
|
|
|
private $hashModifier = ''; |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* @since 2.5 |
127
|
|
|
* |
128
|
|
|
* @param Store $store |
129
|
|
|
* @param QueryFactory $queryFactory |
130
|
|
|
* @param BlobStore $blobStore |
131
|
|
|
* @param BufferedStatsdCollector $bufferedStatsdCollector |
132
|
|
|
*/ |
133
|
235 |
|
public function __construct( Store $store, QueryFactory $queryFactory, BlobStore $blobStore, BufferedStatsdCollector $bufferedStatsdCollector ) { |
134
|
235 |
|
$this->store = $store; |
135
|
235 |
|
$this->queryFactory = $queryFactory; |
136
|
235 |
|
$this->blobStore = $blobStore; |
137
|
235 |
|
$this->bufferedStatsdCollector = $bufferedStatsdCollector; |
138
|
235 |
|
$this->tempCache = ApplicationFactory::getInstance()->getInMemoryPoolCache()->getPoolCacheById( self::POOLCACHE_ID ); |
|
|
|
|
139
|
|
|
|
140
|
235 |
|
$this->initStats( date( 'Y-m-d H:i:s' ) ); |
141
|
235 |
|
} |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* @since 2.5 |
145
|
|
|
* |
146
|
|
|
* @return array |
147
|
|
|
*/ |
148
|
1 |
|
public function getStats() { |
149
|
|
|
|
150
|
|
|
$stats = array_filter( $this->bufferedStatsdCollector->getStats(), function( $key ) { |
151
|
1 |
|
return $key !== false; |
152
|
1 |
|
} ); |
153
|
|
|
|
154
|
1 |
|
if ( !isset( $stats['misses'] ) || ! isset( $stats['hits'] ) ) { |
155
|
|
|
return $stats; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
$misses = $this->sum( 0, $stats['misses'] ); |
159
|
1 |
|
$hits = $this->sum( 0, $stats['hits'] ); |
160
|
1 |
|
|
161
|
|
|
$stats['ratio'] = array(); |
162
|
1 |
|
$stats['ratio']['hit'] = $hits > 0 ? round( $hits / ( $hits + $misses ), 4 ) : 0; |
163
|
1 |
|
$stats['ratio']['miss'] = $hits > 0 ? round( 1 - $stats['ratio']['hit'], 4 ) : 1; |
164
|
|
|
|
165
|
|
|
// Move to last |
166
|
1 |
|
$meta = $stats['meta']; |
167
|
1 |
|
unset( $stats['meta'] ); |
168
|
1 |
|
$stats['meta'] = $meta; |
169
|
|
|
|
170
|
1 |
|
return $stats; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* @since 2.5 |
175
|
|
|
* |
176
|
|
|
* @param string|integer $hashModifier |
177
|
|
|
*/ |
178
|
227 |
|
public function setHashModifier( $hashModifier ) { |
179
|
227 |
|
$this->hashModifier = $hashModifier; |
|
|
|
|
180
|
227 |
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* @see LoggerAwareInterface::setLogger |
184
|
|
|
* |
185
|
|
|
* @since 2.5 |
186
|
|
|
* |
187
|
|
|
* @param LoggerInterface $logger |
188
|
|
|
*/ |
189
|
227 |
|
public function setLogger( LoggerInterface $logger ) { |
190
|
227 |
|
$this->logger = $logger; |
|
|
|
|
191
|
227 |
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* @since 2.5 |
195
|
|
|
* |
196
|
|
|
* @param QueryEngine $queryEngine |
197
|
|
|
*/ |
198
|
138 |
|
public function setQueryEngine( QueryEngine $queryEngine ) { |
199
|
138 |
|
$this->queryEngine = $queryEngine; |
200
|
138 |
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @since 2.5 |
204
|
|
|
* |
205
|
|
|
* @param boolean |
206
|
|
|
*/ |
207
|
235 |
|
public function isEnabled() { |
208
|
235 |
|
return $this->blobStore->canUse(); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* @since 2.5 |
213
|
|
|
* |
214
|
|
|
* @param QueryEngine $queryEngine |
|
|
|
|
215
|
|
|
*/ |
216
|
|
|
public function disableCache() { |
217
|
|
|
$this->enabledCache = false; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* @since 2.5 |
222
|
|
|
*/ |
223
|
135 |
|
public function recordStats() { |
224
|
135 |
|
$this->bufferedStatsdCollector->recordStats(); |
225
|
135 |
|
} |
226
|
|
|
|
227
|
|
|
/** |
228
|
|
|
* @since 2.5 |
229
|
|
|
* |
230
|
|
|
* @param integer|boolean $nonEmbeddedCacheLifetime |
231
|
|
|
*/ |
232
|
227 |
|
public function setNonEmbeddedCacheLifetime( $nonEmbeddedCacheLifetime ) { |
233
|
227 |
|
$this->nonEmbeddedCacheLifetime = $nonEmbeddedCacheLifetime; |
234
|
227 |
|
} |
235
|
|
|
|
236
|
|
|
/** |
237
|
|
|
* @see QueryEngine::getQueryResult |
238
|
|
|
* @since 2.5 |
239
|
|
|
* |
240
|
|
|
* {@inheritDoc} |
241
|
|
|
*/ |
242
|
7 |
|
public function getQueryResult( Query $query ) { |
243
|
|
|
|
244
|
7 |
|
if ( !$this->queryEngine instanceof QueryEngine ) { |
245
|
1 |
|
throw new RuntimeException( "Missing a QueryEngine instance." ); |
246
|
|
|
} |
247
|
|
|
|
248
|
6 |
|
if ( !$this->canUse( $query ) || $query->getLimit() < 1 || $query->getOption( Query::NO_CACHE ) === true ) { |
249
|
2 |
|
$this->bufferedStatsdCollector->incr( $this->getNoCacheId( $query ) ); |
250
|
2 |
|
return $this->queryEngine->getQueryResult( $query ); |
251
|
|
|
} |
252
|
|
|
|
253
|
4 |
|
Timer::start( __CLASS__ ); |
254
|
|
|
|
255
|
4 |
|
$queryId = $this->getHashFrom( $query->getQueryId() ); |
256
|
|
|
|
257
|
4 |
|
$container = $this->blobStore->read( |
258
|
|
|
$queryId |
259
|
|
|
); |
260
|
|
|
|
261
|
4 |
|
if ( $this->tempCache->contains( $queryId ) || $container->has( 'results' ) ) { |
262
|
4 |
|
return $this->newQueryResultFromCache( $queryId, $query, $container ); |
263
|
|
|
} |
264
|
|
|
|
265
|
4 |
|
$queryResult = $this->queryEngine->getQueryResult( $query ); |
266
|
|
|
|
267
|
4 |
|
$this->tempCache->save( |
268
|
|
|
$queryId, |
269
|
|
|
$queryResult |
270
|
|
|
); |
271
|
|
|
|
272
|
4 |
|
$this->log( |
273
|
4 |
|
__METHOD__ . ' from backend in (sec): ' . Timer::getElapsedTime( __CLASS__, 5 ) . " ($queryId)" |
274
|
|
|
); |
275
|
|
|
|
276
|
4 |
|
if ( $this->canUse( $query ) && $queryResult instanceof QueryResult ) { |
277
|
3 |
|
$this->addQueryResultToCache( $queryResult, $queryId, $container, $query ); |
278
|
|
|
} |
279
|
|
|
|
280
|
4 |
|
return $queryResult; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* @since 2.5 |
285
|
|
|
* |
286
|
|
|
* @param DIWikiPage|array $list |
|
|
|
|
287
|
|
|
* @param string $context |
288
|
|
|
*/ |
289
|
235 |
|
public function resetCacheBy( $item, $context = '' ) { |
290
|
|
|
|
291
|
235 |
|
if ( !is_array( $item ) ) { |
292
|
234 |
|
$item = array( $item ); |
293
|
|
|
} |
294
|
|
|
|
295
|
235 |
|
$recordStats = false; |
296
|
235 |
|
$context = $context === '' ? 'Undefined' : $context; |
297
|
|
|
|
298
|
235 |
|
foreach ( $item as $id ) { |
299
|
235 |
|
$id = $this->getHashFrom( $id ); |
300
|
235 |
|
$this->tempCache->delete( $id ); |
301
|
|
|
|
302
|
235 |
|
if ( $this->blobStore->exists( $id ) ) { |
303
|
211 |
|
$recordStats = true; |
304
|
211 |
|
$this->bufferedStatsdCollector->incr( 'deletes.on' . $context ); |
305
|
235 |
|
$this->blobStore->delete( $id ); |
306
|
|
|
} |
307
|
|
|
} |
308
|
|
|
|
309
|
235 |
|
if ( $recordStats ) { |
310
|
211 |
|
$this->bufferedStatsdCollector->recordStats(); |
311
|
|
|
} |
312
|
235 |
|
} |
313
|
|
|
|
314
|
6 |
|
private function canUse( $query ) { |
315
|
6 |
|
return $this->enabledCache && $this->blobStore->canUse() && ( $query->getContextPage() !== null || ( $query->getContextPage() === null && $this->nonEmbeddedCacheLifetime > 0 ) ); |
316
|
|
|
} |
317
|
|
|
|
318
|
4 |
|
private function newQueryResultFromCache( $queryId, $query, $container ) { |
319
|
|
|
|
320
|
4 |
|
$results = array(); |
321
|
4 |
|
$incrStats = 'hits.Undefined'; |
|
|
|
|
322
|
|
|
|
323
|
4 |
|
if ( ( $context = $query->getOption( Query::PROC_CONTEXT ) ) === false ) { |
324
|
3 |
|
$context = 'Undefined'; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
// Check if the tempCache is available for result that have not yet been |
328
|
|
|
// stored to the cache back-end |
329
|
4 |
|
if ( ( $queryResult = $this->tempCache->fetch( $queryId ) ) !== false ) { |
330
|
1 |
|
$this->log( __METHOD__ . ' using tempCache ' . "($queryId)" ); |
331
|
|
|
|
332
|
1 |
|
if ( !$queryResult instanceof QueryResult ) { |
333
|
1 |
|
return $queryResult; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
$incrStats = 'hits.tempCache.' . ( $query->getContextPage() !== null ? 'embedded' : 'nonEmbedded' ); |
337
|
|
|
|
338
|
|
|
$queryResult->reset(); |
339
|
|
|
$results = $queryResult->getResults(); |
340
|
|
|
|
341
|
|
|
$hasFurtherResults = $queryResult->hasFurtherResults(); |
342
|
|
|
$countValue = $queryResult->getCountValue(); |
343
|
|
|
} else { |
344
|
|
|
|
345
|
|
|
$incrStats = ( $query->getContextPage() !== null ? 'hits.embedded.' : 'hits.nonEmbedded.' ) . $context; |
346
|
|
|
|
347
|
|
|
foreach ( $container->get( 'results' ) as $hash ) { |
348
|
3 |
|
$results[] = DIWikiPage::doUnserialize( $hash ); |
349
|
|
|
} |
350
|
3 |
|
|
351
|
3 |
|
$hasFurtherResults = $container->get( 'continue' ); |
352
|
|
|
$countValue = $container->get( 'count' ); |
353
|
|
|
} |
354
|
3 |
|
|
355
|
3 |
|
$queryResult = $this->queryFactory->newQueryResult( |
356
|
|
|
$this->store, |
357
|
|
|
$query, |
358
|
3 |
|
$results, |
359
|
3 |
|
$hasFurtherResults |
360
|
|
|
); |
361
|
|
|
|
362
|
|
|
$queryResult->setCountValue( $countValue ); |
363
|
|
|
$queryResult->setFromCache( true ); |
364
|
|
|
|
365
|
3 |
|
$time = Timer::getElapsedTime( __CLASS__, 5 ); |
366
|
3 |
|
|
367
|
|
|
$this->bufferedStatsdCollector->incr( $incrStats ); |
368
|
3 |
|
|
369
|
|
|
$this->bufferedStatsdCollector->calcMedian( |
370
|
3 |
|
'medianRetrievalResponseTime.cached', |
371
|
|
|
$time |
372
|
3 |
|
); |
373
|
3 |
|
|
374
|
|
|
$this->log( __METHOD__ . ' (sec): ' . $time . " ($queryId)" ); |
375
|
|
|
|
376
|
|
|
return $queryResult; |
377
|
3 |
|
} |
378
|
|
|
|
379
|
3 |
|
private function addQueryResultToCache( $queryResult, $queryId, $container, $query ) { |
380
|
|
|
|
381
|
|
|
if ( ( $context = $query->getOption( Query::PROC_CONTEXT ) ) === false ) { |
382
|
3 |
|
$context = 'Undefined'; |
383
|
|
|
} |
384
|
3 |
|
|
385
|
|
|
$this->bufferedStatsdCollector->incr( |
386
|
3 |
|
( $query->getContextPage() !== null ? 'misses.embedded.' : 'misses.nonEmbedded.' ) . $context |
387
|
3 |
|
); |
388
|
3 |
|
|
389
|
|
|
$this->bufferedStatsdCollector->calcMedian( |
390
|
|
|
'medianRetrievalResponseTime.uncached', |
391
|
3 |
|
Timer::getElapsedTime( __CLASS__, 5 ) |
392
|
3 |
|
); |
393
|
3 |
|
|
394
|
|
|
$callback = function() use( $queryResult, $queryId, $container, $query ) { |
395
|
3 |
|
$this->doCacheQueryResult( $queryResult, $queryId, $container, $query ); |
396
|
|
|
}; |
397
|
|
|
|
398
|
|
|
$deferredCallableUpdate = ApplicationFactory::getInstance()->newDeferredCallableUpdate( |
399
|
3 |
|
$callback |
400
|
3 |
|
); |
401
|
3 |
|
|
402
|
3 |
|
$deferredCallableUpdate->setOrigin( __METHOD__ ); |
403
|
|
|
$deferredCallableUpdate->setFingerprint( __METHOD__ . $queryId ); |
404
|
3 |
|
$deferredCallableUpdate->pushUpdate(); |
405
|
|
|
} |
406
|
3 |
|
|
407
|
|
|
private function doCacheQueryResult( $queryResult, $queryId, $container, $query ) { |
408
|
|
|
|
409
|
|
|
$results = array(); |
410
|
3 |
|
|
411
|
3 |
|
// Keep the simple string representation to avoid unnecessary data cruft |
412
|
|
|
// during using PHP serialize( ... ) |
413
|
|
|
foreach ( $queryResult->getResults() as $dataItem ) { |
414
|
3 |
|
$results[] = $dataItem->getSerialization(); |
415
|
3 |
|
} |
416
|
3 |
|
|
417
|
|
|
$container->set( 'results', $results ); |
418
|
3 |
|
$container->set( 'continue', $queryResult->hasFurtherResults() ); |
419
|
3 |
|
$container->set( 'count', $queryResult->getCountValue() ); |
420
|
|
|
|
421
|
3 |
|
$queryResult->reset(); |
422
|
|
|
$contextPage = $query->getContextPage(); |
423
|
|
|
|
424
|
|
|
if ( $contextPage === null ) { |
425
|
3 |
|
$container->setExpiryInSeconds( $this->nonEmbeddedCacheLifetime ); |
426
|
3 |
|
$hash = 'nonEmbedded'; |
|
|
|
|
427
|
|
|
} else { |
428
|
|
|
$this->addToLinkedList( $contextPage, $queryId ); |
429
|
3 |
|
$hash = $contextPage->getHash(); |
|
|
|
|
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
$this->blobStore->save( |
433
|
3 |
|
$container |
434
|
|
|
); |
435
|
3 |
|
|
436
|
3 |
|
$this->tempCache->delete( $queryId ); |
437
|
|
|
|
438
|
|
|
$this->log( |
439
|
3 |
|
__METHOD__ . ' cache storage (sec): ' . Timer::getElapsedTime( __CLASS__, 5 ) . " ($queryId)" |
440
|
|
|
); |
441
|
|
|
|
442
|
3 |
|
return $queryResult; |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
private function addToLinkedList( $contextPage, $queryId ) { |
446
|
3 |
|
|
447
|
3 |
|
// Ensure that without QueryDependencyLinksStore being enabled recorded |
448
|
|
|
// subjects related to a query can be discoverable and purged separately |
449
|
|
|
$container = $this->blobStore->read( |
450
|
|
|
$this->getHashFrom( $contextPage ) |
451
|
|
|
); |
452
|
3 |
|
|
453
|
|
|
// If a subject gets purged then the linked list of queries associated |
454
|
3 |
|
// with that subject allows for an immediate associated removal |
455
|
|
|
$container->addToLinkedList( $queryId ); |
456
|
|
|
|
457
|
3 |
|
$this->blobStore->save( |
458
|
|
|
$container |
459
|
236 |
|
); |
460
|
|
|
} |
461
|
236 |
|
|
462
|
234 |
|
private function getHashFrom( $subject ) { |
463
|
|
|
|
464
|
|
|
if ( $subject instanceof DIWikiPage ) { |
465
|
236 |
|
$subject = $subject->asBase()->getHash(); |
466
|
|
|
} |
467
|
|
|
|
468
|
4 |
|
return md5( $subject . self::VERSION . $this->hashModifier ); |
469
|
|
|
} |
470
|
4 |
|
|
471
|
1 |
|
private function log( $message, $context = array() ) { |
472
|
|
|
|
473
|
|
|
if ( $this->logger === null ) { |
474
|
3 |
|
return; |
475
|
3 |
|
} |
476
|
|
|
|
477
|
2 |
|
$this->logger->info( $message, $context ); |
478
|
|
|
} |
479
|
2 |
|
|
480
|
|
|
private function getNoCacheId( $query ) { |
481
|
2 |
|
|
482
|
1 |
|
$id = 'noCache.misc'; |
483
|
|
|
|
484
|
|
|
if ( !$this->canUse( $query ) ) { |
485
|
2 |
|
$id = 'noCache.disabled'; |
486
|
1 |
|
} |
487
|
|
|
|
488
|
|
|
if ( $query->getLimit() < 1 ) { |
489
|
2 |
|
$id = 'noCache.byLimit'; |
490
|
1 |
|
} |
491
|
|
|
|
492
|
|
|
if ( $query->getOption( Query::NO_CACHE ) === true ) { |
493
|
2 |
|
$id = 'noCache.byOption'; |
494
|
|
|
} |
495
|
|
|
|
496
|
235 |
|
if ( ( $context = $query->getOption( Query::PROC_CONTEXT ) ) !== false ) { |
497
|
|
|
$id .= '.' . $context; |
498
|
235 |
|
} |
499
|
|
|
|
500
|
235 |
|
return $id; |
501
|
235 |
|
} |
502
|
235 |
|
|
503
|
235 |
|
private function initStats( $date ) { |
|
|
|
|
504
|
235 |
|
|
505
|
235 |
|
$this->bufferedStatsdCollector->shouldRecord( $this->isEnabled() ); |
506
|
235 |
|
|
507
|
235 |
|
$this->bufferedStatsdCollector->init( 'misses', array() ); |
|
|
|
|
508
|
235 |
|
$this->bufferedStatsdCollector->init( 'hits', array() ); |
|
|
|
|
509
|
235 |
|
$this->bufferedStatsdCollector->init( 'deletes', array() ); |
|
|
|
|
510
|
235 |
|
$this->bufferedStatsdCollector->init( 'noCache', array() ); |
|
|
|
|
511
|
|
|
$this->bufferedStatsdCollector->init( 'medianRetrievalResponseTime', array() ); |
|
|
|
|
512
|
|
|
$this->bufferedStatsdCollector->set( 'meta.version', self::VERSION ); |
513
|
|
|
$this->bufferedStatsdCollector->set( 'meta.cacheLifetime.embedded', $GLOBALS['smwgQueryResultCacheLifetime'] ); |
514
|
|
|
$this->bufferedStatsdCollector->set( 'meta.cacheLifetime.nonEmbedded', $GLOBALS['smwgQueryResultNonEmbeddedCacheLifetime'] ); |
515
|
|
|
$this->bufferedStatsdCollector->init( 'meta.collectionDate.start', $date ); |
516
|
|
|
$this->bufferedStatsdCollector->set( 'meta.collectionDate.update', $date ); |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
// http://stackoverflow.com/questions/3777995/php-array-recursive-sum |
520
|
|
|
private static function sum( $value, $container ) { |
521
|
|
|
return $value + ( is_array( $container ) ? array_reduce( $container, 'self::sum' ) : $container ); |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
} |
525
|
|
|
|
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..