Skip to content

Commit c488171

Browse files
Make APC updateGauge with set command lock-free in critical path
When setting a value on a gauge, apcu_store is always called two times, taking a write lock each that can cause contention. - Replace apcu_store with compare-and-swap algorithm unless we store for the first time(s) - Eagerly fetch old value instead of apcu_exists, since we will anyway need it for the compare-and-swap algorithms that follows, saving one call to APCu - Make sure we never enter an infinite loop in the compare-and-swap section by falling back to a first-time insert if $old === false. A fix for the potential infinite loop below will be done in a separate commit. Signed-off-by: Tobias Bengtsson <me@tobiasbengtsson.se>
1 parent 7538b4d commit c488171

File tree

1 file changed

+24
-3
lines changed

1 file changed

+24
-3
lines changed

src/Prometheus/Storage/APC.php

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,32 @@ public function updateSummary(array $data): void
123123
public function updateGauge(array $data): void
124124
{
125125
$valueKey = $this->valueKey($data);
126+
$old = apcu_fetch($valueKey);
126127
if ($data['command'] === Adapter::COMMAND_SET) {
127-
apcu_store($valueKey, $this->toBinaryRepresentationAsInteger($data['value']));
128-
apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
128+
$new = $this->toBinaryRepresentationAsInteger($data['value']);
129+
if ($old === false) {
130+
apcu_store($valueKey, $new);
131+
apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
132+
return;
133+
} else {
134+
// Taken from https://github.com/prometheus/client_golang/blob/66058aac3a83021948e5fb12f1f408ff556b9037/prometheus/value.go#L91
135+
while (true) {
136+
if ($old !== false) {
137+
if (apcu_cas($valueKey, $old, $new)) {
138+
return;
139+
} else {
140+
$old = apcu_fetch($valueKey);
141+
}
142+
} else {
143+
// Cache got evicted under our feet? Just consider it a fresh/new insert and move on.
144+
apcu_store($valueKey, $new);
145+
apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
146+
return;
147+
}
148+
}
149+
}
129150
} else {
130-
if (!apcu_exists($valueKey)) {
151+
if ($old === false) {
131152
$new = apcu_add($valueKey, $this->toBinaryRepresentationAsInteger(0));
132153
if ($new) {
133154
apcu_store($this->metaKey($data), json_encode($this->metaData($data)));

0 commit comments

Comments
 (0)