Skip to content

Commit

Permalink
Make APC updateGauge with set command lock-free in critical path
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
TobiasBengtsson committed Nov 9, 2023
1 parent d57ca28 commit a5a5b74
Showing 1 changed file with 24 additions and 3 deletions.
27 changes: 24 additions & 3 deletions src/Prometheus/Storage/APC.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,32 @@ public function updateSummary(array $data): void
public function updateGauge(array $data): void
{
$valueKey = $this->valueKey($data);
$old = apcu_fetch($valueKey);
if ($data['command'] === Adapter::COMMAND_SET) {
apcu_store($valueKey, $this->toBinaryRepresentationAsInteger($data['value']));
apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
$new = $this->toBinaryRepresentationAsInteger($data['value']);
if ($old === false) {
apcu_store($valueKey, $new);
apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
return;
} else {
// Taken from https://github.com/prometheus/client_golang/blob/66058aac3a83021948e5fb12f1f408ff556b9037/prometheus/value.go#L91
while (true) {
if ($old !== false) {
if (apcu_cas($valueKey, $old, $new)) {
return;
} else {
$old = apcu_fetch($valueKey);
}
} else {
// Cache got evicted under our feet? Just consider it a fresh/new insert and move on.
apcu_store($valueKey, $new);
apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
return;
}
}
}
} else {
if (!apcu_exists($valueKey)) {
if ($old === false) {
$new = apcu_add($valueKey, $this->toBinaryRepresentationAsInteger(0));
if ($new) {
apcu_store($this->metaKey($data), json_encode($this->metaData($data)));
Expand Down

0 comments on commit a5a5b74

Please sign in to comment.