Shared\Counter
OxPHP\Shared\Counter — це атомарне 64-бітне ціле число зі знаком, спільне для всього процесу й призначене для накопичення: підрахунку подій, підсумовування приростів, ковзних підсумків за вікно. Усі операції неблокувальні: два воркери, що додають одночасно, ніколи не втратять жодного інкременту.
Для довільного атомарного стану, який має синхронізувати іншу пам’ять (скінченні автомати, мітки версій, seqlock-и, маски бітових прапорців), використовуйте натомість Shared\Atomic.
Огляд
- Атомарний int64. Діапазон
−9_223_372_036_854_775_808 … 9_223_372_036_854_775_807. Переповнення циклічне. - Неблокувальний.
addкомпілюється в одну інструкціюfetch_add. - Завжди Relaxed. Операції атомарні (жодних втрачених інкрементів, жодних розірваних читань), але не встановлюють відношення happens-before з іншою пам’яттю. Counter — це статистика, а не точка синхронізації: якщо вам потрібен порядок, використовуйте
Shared\Atomic. - Shareable. Екземпляри можна зберігати всередині
Shared\Map/Shared\Channelі передавати у файбери через захопленняuse.
Довідник API
namespace OxPHP\Shared;
final class Counter implements Shareable
{
public function __construct(int $initial = 0);
public function get(): int; // current
public function set(int $value): int; // returns previous; set(0) = window reset
public function add(int $delta = 1): int; // returns new; add()=+1, add(-1)=decrement
public function compareAndSet(int $expect, int $new): bool;
public function id(): int;
}| Метод | Повертає | Призначення |
|---|---|---|
get |
поточне | Читання без зміни. |
set |
попереднє | Атомарний обмін; set(0) — читання з обнуленням наприкінці вікна. |
add |
нове | add() збільшує на 1, add(-1) зменшує, інакше — будь-який приріст. |
compareAndSet |
bool | Обмежені / насичувальні лічильники (стеля, підлога) через цикл CAS. |
id |
id у реєстрі | Логування, трасування, зіставлення з /__ox_shared/entry?id=…. |
Приклади
Лічильник запитів воркера
<?php
$requests = new OxPHP\Shared\Counter();
oxphp_worker(function () use ($requests) {
$count = $requests->add(); // +1, returns the new total
header("X-Request-Count: {$count}");
echo "ok";
});Перехід до нового вікна
<?php
$hits = new OxPHP\Shared\Counter();
// Every N minutes in your cron/worker loop:
$prev = $hits->set(0); // atomically reads and zeroes
logWindowMetric($prev);Обмежений лічильник (цикл CAS)
<?php
$slots = new OxPHP\Shared\Counter();
$cap = 100;
// Claim a slot only while under the cap.
do {
$cur = $slots->get();
if ($cur >= $cap) {
// full — reject
break;
}
} while (!$slots->compareAndSet($cur, $cur + 1));Пакетне накопичення
<?php
$bytes = new OxPHP\Shared\Counter();
// Sum a batch in PHP, then one atomic add (one FFI call).
$deltas = array_map(fn ($req) => strlen($req['body']), $batch);
$newTotal = $bytes->add(array_sum($deltas));Семантика та підводні камені
set() повертає попереднє значення, а потім записує нове — атомарно. set(0) — це патерн «знімок і обнулення» (LongAdder::sumThenReset); set($n) задає будь-яку нову початкову точку.
Кожна операція атомарна, але Counter не публікує іншу пам’ять. Якщо читач має побачити дані, які записувач записав до збільшення числа, — це вже синхронізація: використовуйте Shared\Atomic з Ordering::Release/Acquire.
compareAndSet працює з порядком Relaxed/Relaxed і не приймає аргументів порядку. Він коректний для рішень, що ухвалюються на основі власного значення лічильника (стеля, підлога, захоплення за значенням). CAS, що публікує інший стан, — це справа Shared\Atomic.
Додавання понад INT_MAX переходить до INT_MIN. Для лічильників, що працюють місяцями з тисячами операцій на секунду, тримайте значення в межах десятків трильйонів або періодично скидайте його.
Жодних дробових значень. Рахуєте байти, щоб отримати середні з точністю float? Відстежуйте чисельник (Counter) і знаменник (Counter) окремо й діліть під час читання.
Винятки
| Виняток | Коли виникає |
|---|---|
StaleHandleException |
Будь-який метод дескриптора, чий запис у реєстрі витіснено. |
UninitializedException |
id() в обгортці, яка ще не завершила __construct. |
Лічильники ніколи не кидають винятків через переповнення чи екстремальні значення — вони циклічно переходять.
Спостережуваність
Повний огляд — у розділі Спостережуваність спільного стану. Коротка довідка:
GET /__ox_shared/entry?id=Nпоказує{ value, type: "Counter" }.- Gauge Prometheus
oxphp_shared_counter_value{counter_id="…"}відстежує поточне значення. - Лічильники всього реєстру (
oxphp_shared_operations_total,oxphp_shared_objects_total) охоплюють Counter через міткуtype="Counter".
Коли не використовувати
- Числа з рухомою комою чи десяткові дроби. Використовуйте пару Counter (чисельник / знаменник) або
Shared\Mutex<array{total_cents: int, count: int}>. - Нечислові події, яким потрібен багатий контекст. Якщо вам потрібні
{count, last_actor, last_reason}, пов’язані з одним ключем, берітьShared\MapабоShared\Mutex. - Підсумки між хостами. Counter існує лише в межах процесу. Для агрегації з кількох хостів використовуйте конвеєр метрик (Prometheus +
rate()або центральний RedisINCR). - Довговічність. Стан Counter зникає під час зупинки сервера. Якщо підсумок має пережити перезапуск, зберігайте знімки деінде.
Промпти для вашого агента
Ці промпти розраховані на агента з підключеним MCP-сервером документації OxPHP — потрібні сторінки він прочитає сам.
Використай OxPHP Shared Counter у моєму коді замість інкременту APCu чи статичної властивості, якими я зараз підраховую запити, звернення або використання за орендарями. Прочитай сторінку `shared-state/shared-counter` через MCP-сервер документації OxPHP.
Знайди, де в цьому репозиторії так рахують, і покажи мені заміну на Counter, зокрема прив’язку `Registry::counter`, яка зберігає один спільний підсумок для всіх воркерів.Пов’язані сторінки
- Спільний стан — огляд і патерни міграції.
- Shared\Atomic — універсальний атомарний int64 із CAS, swap і повним керуванням порядком пам’яті.
- Shared\Map — коли лічильники прив’язані до ключів (
Map<string, Counter>). - Shared\Flag — коли значення лише «увімкнено/вимкнено».
- Shared\Mutex — коли лічильник має оновлюватися синхронно з іншими полями.