Skip to content

Commit

Permalink
Make APCng 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, first apcu_store is called in
updateGauge and then apcu_add is called in storeMetadata. Both of
these operations takes a write lock in APCu which can cause contention.

- Change updateGauge to use a compare-and-swap algorithm instead of
apcu_store, unless we're adding something for the first time(s) in
which case we just set it with apcu_store.
- Don't call updateMetadata and storeLabelKeys unless it's the first
time we're setting a gauge (matches the behaviour of inc/dec).
- For extra safety, make a cheap rlock-only check in storeMetadata if
the key exists before calling apcu_add with a wlock.
  • Loading branch information
TobiasBengtsson committed Nov 9, 2023
1 parent 735ace7 commit d57ca28
Showing 1 changed file with 20 additions and 5 deletions.
25 changes: 20 additions & 5 deletions src/Prometheus/Storage/APCng.php
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,27 @@ 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->convertToIncrementalInteger($data['value']), 0);
$this->storeMetadata($data);
$this->storeLabelKeys($data);
$new = $this->convertToIncrementalInteger($data['value']);
if ($old === false) {
apcu_store($valueKey, $new, 0);
$this->storeMetadata($data);
$this->storeLabelKeys($data);

return;
}

for ($loops = 0; $loops < self::MAX_LOOPS; $loops++) {
if (apcu_cas($valueKey, $old, $new)) {
break;
}
$old = apcu_fetch($valueKey);
}

return;
}

$old = apcu_fetch($valueKey);

if ($old === false) {
apcu_add($valueKey, 0, 0);
$this->storeMetadata($data);
Expand Down Expand Up @@ -896,6 +907,10 @@ private function decodeLabelKey(string $str): string
private function storeMetadata(array $data, bool $encoded = true): void
{
$metaKey = $this->metaKey($data);
if (apcu_exists($metaKey)) {
return;
}

$metaData = $this->metaData($data);
$toStore = $metaData;

Expand Down

0 comments on commit d57ca28

Please sign in to comment.