Completed
Push — master ( 8d3377...5d9cd9 )
by mw
232:45 queued 197:48
created

BufferedStatsdCollector::recordStats()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 24
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 1
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
1
<?php
2
3
namespace SMW\Utils;
4
5
use Onoi\BlobStore\BlobStore;
6
use SMW\ApplicationFactory;
7
use RuntimeException;
8
9
/**
10
 * Collect statistics in a provisional schema-free storage that depends on the
11
 * availability of the cache back-end.
12
 *
13
 * @license GNU GPL v2+
14
 * @since 2.5
15
 *
16
 * @author mwjames
17
 */
18
class BufferedStatsdCollector {
19
20
	/**
21
	 * Update this version number when the serialization format
22
	 * changes.
23
	 */
24
	const VERSION = '0.2';
25
26
	/**
27
	 * Available operations
28
	 */
29
	const STATS_INIT = 'init';
30
	const STATS_INCR = 'incr';
31
	const STATS_SET = 'set';
32
	const STATS_MEDIAN = 'median';
33
34
	/**
35
	 * Namespace occupied by the BlobStore
36
	 */
37
	const CACHE_NAMESPACE = 'smw:stats:store';
38
39
	/**
40
	 * @var BlobStore
41
	 */
42
	private $blobStore;
43
44
	/**
45
	 * @var string|integer
46
	 */
47
	private $statsdId;
48
49
	/**
50
	 * @var boolean
51
	 */
52
	private $shouldRecord = true;
53
54
	/**
55
	 * @var array
56
	 */
57
	private $stats = array();
58
59
	/**
60
	 * Identifies an update fingerprint to compare invoked deferred updates
61
	 * against each other and filter those with the same print to avoid recording
62
	 * duplicate stats.
63
	 *
64
	 * @var string
65
	 */
66
	private $fingerprint = null;
67
68
	/**
69
	 * @var array
70
	 */
71
	private $operations = array();
72
73
	/**
74
	 * @since 2.5
75
	 *
76
	 * @param BlobStore $blobStore
77
	 * @param string $statsdId
78
	 */
79
	public function __construct( BlobStore $blobStore, $statsdId ) {
80
		$this->blobStore = $blobStore;
81
		$this->statsdId = $statsdId;
82
		$this->fingerprint = $statsdId . uniqid();
83
	}
84
85
	/**
86
	 * @since 2.5
87
	 *
88
	 * @param boolean $shouldRecord
89
	 */
90
	public function shouldRecord( $shouldRecord ) {
91
		$this->shouldRecord = (bool)$shouldRecord;
92
	}
93
94
	/**
95
	 * @since 2.5
96
	 *
97
	 * @return array
98
	 */
99
	public function getStats() {
100
101
		$container = $this->blobStore->read(
102
			md5( $this->statsdId . self::VERSION )
103
		);
104
105
		return StatsFormatter::getStatsFromFlatKey( $container->getData(), '.' );
106
	}
107
108
	/**
109
	 * @since 2.5
110
	 *
111
	 * @param string|array $key
112
	 */
113
	public function incr( $key ) {
114
115
		if ( !isset( $this->stats[$key] ) ) {
116
			$this->stats[$key] = 0;
117
		}
118
119
		$this->stats[$key]++;
120
		$this->operations[$key] = self::STATS_INCR;
121
	}
122
123
	/**
124
	 * @since 2.5
125
	 *
126
	 * @param string|array $key
127
	 * @param string|integer $default
128
	 */
129
	public function init( $key, $default ) {
130
		$this->stats[$key] = $default;
131
		$this->operations[$key] = self::STATS_INIT;
132
	}
133
134
	/**
135
	 * @since 2.5
136
	 *
137
	 * @param string|array $key
138
	 * @param string|integer $value
139
	 */
140
	public function set( $key, $value ) {
141
		$this->stats[$key] = $value;
142
		$this->operations[$key] = self::STATS_SET;
143
	}
144
145
	/**
146
	 * @since 2.5
147
	 *
148
	 * @param string|array $key
149
	 * @param integer $value
150
	 */
151
	public function calcMedian( $key, $value ) {
152
153
		if ( !isset( $this->stats[$key] ) ) {
154
			$this->stats[$key] = $value;
155
		} else {
156
			$this->stats[$key] = ( $this->stats[$key] + $value ) / 2;
157
		}
158
159
		$this->operations[$key] = self::STATS_MEDIAN;
160
	}
161
162
	/**
163
	 * @since 2.5
164
	 */
165
	public function saveStats() {
166
167
		if ( $this->stats === array() ) {
168
			return;
169
		}
170
171
		$container = $this->blobStore->read(
172
			md5( $this->statsdId . self::VERSION )
173
		);
174
175
		foreach ( $this->stats as $key => $value ) {
176
177
			$old = $container->has( $key ) ? $container->get( $key ) : 0;
178
179
			if ( $this->operations[$key] === self::STATS_INIT && $old != 0 ) {
180
				$value = $old;
181
			}
182
183
			if ( $this->operations[$key] === self::STATS_INCR ) {
184
				$value = $old + $value;
185
			}
186
187
			// Use as-is
188
			// $this->operations[$key] === self::STATS_SET
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
189
190
			if ( $this->operations[$key] === self::STATS_MEDIAN ) {
191
				$value = $old > 0 ? ( $old + $value ) / 2 : $value;
192
			}
193
194
			$container->set( $key, $value );
195
		}
196
197
		$this->blobStore->save(
198
			$container
199
		);
200
201
		$this->stats = array();
202
	}
203
204
	/**
205
	 * @since 2.5
206
	 *
207
	 * @param boolean $asPending
208
	 */
209
	public function recordStats( $asPending = false ) {
210
211
		if ( $this->shouldRecord === false ) {
212
			return $this->stats = array();
213
		}
214
215
		// #2046
216
		// __destruct as event trigger has shown to be unreliable in a MediaWiki
217
		// environment therefore rely on the deferred update and any caller
218
		// that invokes the recordStats method
219
220
		$deferredCallableUpdate = ApplicationFactory::getInstance()->newDeferredCallableUpdate(
221
			function() { $this->saveStats(); }
222
		);
223
224
		$deferredCallableUpdate->setOrigin( __METHOD__ );
225
226
		$deferredCallableUpdate->setFingerprint(
227
			__METHOD__ . $this->fingerprint
228
		);
229
230
		$deferredCallableUpdate->markAsPending( $asPending );
231
		$deferredCallableUpdate->pushToUpdateQueue();
232
	}
233
234
}
235