Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/infrastructure/util/PhabricatorMetronome.php
12241 views
1
<?php
2
3
/**
4
* Tick at a given frequency with a specifiable offset.
5
*
6
* One use case for this is to flatten out load spikes caused by periodic
7
* service calls. Give each host a metronome that ticks at the same frequency,
8
* but with different offsets. Then, have hosts make service calls only after
9
* their metronome ticks. This spreads service calls out evenly more quickly
10
* and more predictably than adding random jitter.
11
*/
12
final class PhabricatorMetronome
13
extends Phobject {
14
15
private $offset = 0;
16
private $frequency;
17
18
public function setOffset($offset) {
19
if (!is_int($offset)) {
20
throw new Exception(pht('Metronome offset must be an integer.'));
21
}
22
23
if ($offset < 0) {
24
throw new Exception(pht('Metronome offset must be 0 or more.'));
25
}
26
27
// We're not requiring that the offset be smaller than the frequency. If
28
// the offset is larger, we'll just clamp it to the frequency before we
29
// use it. This allows the offset to be configured before the frequency
30
// is configured, which is useful for using a hostname as an offset seed.
31
32
$this->offset = $offset;
33
34
return $this;
35
}
36
37
public function setFrequency($frequency) {
38
if (!is_int($frequency)) {
39
throw new Exception(pht('Metronome frequency must be an integer.'));
40
}
41
42
if ($frequency < 1) {
43
throw new Exception(pht('Metronome frequency must be 1 or more.'));
44
}
45
46
$this->frequency = $frequency;
47
48
return $this;
49
}
50
51
public function setOffsetFromSeed($seed) {
52
$offset = PhabricatorHash::digestToRange($seed, 0, 0x7FFFFFFF);
53
return $this->setOffset($offset);
54
}
55
56
public function getFrequency() {
57
if ($this->frequency === null) {
58
throw new PhutilInvalidStateException('setFrequency');
59
}
60
return $this->frequency;
61
}
62
63
public function getOffset() {
64
$frequency = $this->getFrequency();
65
return ($this->offset % $frequency);
66
}
67
68
public function getNextTickAfter($epoch) {
69
$frequency = $this->getFrequency();
70
$offset = $this->getOffset();
71
72
$remainder = ($epoch % $frequency);
73
74
if ($remainder < $offset) {
75
return ($epoch - $remainder) + $offset;
76
} else {
77
return ($epoch - $remainder) + $frequency + $offset;
78
}
79
}
80
81
public function didTickBetween($min, $max) {
82
if ($max < $min) {
83
throw new Exception(
84
pht(
85
'Maximum tick window must not be smaller than minimum tick window.'));
86
}
87
88
$next = $this->getNextTickAfter($min);
89
return ($next <= $max);
90
}
91
92
}
93
94