Skip to content

Commit 7538b4d

Browse files
Make APCng updateGauge with set command lock-free in critical path
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. Signed-off-by: Tobias Bengtsson <me@tobiasbengtsson.se>
1 parent 735ace7 commit 7538b4d

File tree

1 file changed

+27
-5
lines changed

1 file changed

+27
-5
lines changed

src/Prometheus/Storage/APCng.php

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,16 +169,34 @@ public function updateSummary(array $data): void
169169
public function updateGauge(array $data): void
170170
{
171171
$valueKey = $this->valueKey($data);
172+
$old = apcu_fetch($valueKey);
172173
if ($data['command'] === Adapter::COMMAND_SET) {
173-
apcu_store($valueKey, $this->convertToIncrementalInteger($data['value']), 0);
174-
$this->storeMetadata($data);
175-
$this->storeLabelKeys($data);
174+
$new = $this->convertToIncrementalInteger($data['value']);
175+
if ($old === false) {
176+
apcu_store($valueKey, $new, 0);
177+
$this->storeMetadata($data);
178+
$this->storeLabelKeys($data);
179+
180+
return;
181+
}
182+
183+
for ($loops = 0; $loops < self::MAX_LOOPS; $loops++) {
184+
if (apcu_cas($valueKey, $old, $new)) {
185+
break;
186+
}
187+
$old = apcu_fetch($valueKey);
188+
if ($old === false) {
189+
apcu_store($valueKey, $new, 0);
190+
$this->storeMetadata($data);
191+
$this->storeLabelKeys($data);
192+
193+
return;
194+
}
195+
}
176196

177197
return;
178198
}
179199

180-
$old = apcu_fetch($valueKey);
181-
182200
if ($old === false) {
183201
apcu_add($valueKey, 0, 0);
184202
$this->storeMetadata($data);
@@ -896,6 +914,10 @@ private function decodeLabelKey(string $str): string
896914
private function storeMetadata(array $data, bool $encoded = true): void
897915
{
898916
$metaKey = $this->metaKey($data);
917+
if (apcu_exists($metaKey)) {
918+
return;
919+
}
920+
899921
$metaData = $this->metaData($data);
900922
$toStore = $metaData;
901923

0 commit comments

Comments
 (0)