Угоди про іменування OxPHP\Shared\*
Простір імен OxPHP\Shared\* — це API паралелізму на рівні застосунку:
Atomic, Counter, Flag, Map, Channel, Mutex, Once, Pool.
Назви методів підпорядковуються єдиному набору правил, тож користувачі можуть
передбачити API, не звертаючись до документації щодо кожного типу.
Цей документ є канонічним довідником. Нові примітиви, а також зміни в наявних, ПОВИННІ йому відповідати.
Правила
1. Читання значення — get()
Угода PHP. Використовується в Map::get(), Counter::get(), Once::get().
Atomic::load(Ordering $order = Ordering::SeqCst) — свідомий виняток:
сама його наявність несе аргумент упорядкування й сигналізує, що читання
є частиною контракту моделі пам’яті, відмінного від звичайного геттера.
2. Запис значення — set(), store() для атомарних типів
Map::set(), оновлення значення Mutex (за посиланням усередині withLock), Once::getOrInit().
Atomic::store(int $value, Ordering $order = Ordering::SeqCst) віддзеркалює load з тієї ж причини.
3. Кількість елементів — count(): int
Кожен контейнер, що відкриває свій поточний розмір, робить це під назвою
count(): int. Channel додатково реалізує \Countable, тож
count($ch) працює як нативна ідіома для елементів у черзі. Map
відкриває count(): int як метод, але не реалізує \Countable —
викликайте його безпосередньо. Pool не має count(); кількість живих
слотів читайте з stats()->size():
$ch = new OxPHP\Shared\Channel(1024);
$map = new OxPHP\Shared\Map();
$pool = new OxPHP\Shared\Pool($factory);
count($ch); // queued items (Channel implements \Countable)
$map->count(); // entries
$pool->stats()->size(); // total live slots (in-use + idle)Жодних size(), len() чи pending() — вони заборонені в
публічному інтерфейсі, хоч би з якої мови походила м’язова пам’ять
розробника.
4. Булевий геттер — префікс is*()
Channel::isClosed().
Жодних голих дієслів (test, check) і жодних предметно-специфічних назв
(closed). Префікс is позначає чисте читання булевої властивості.
Тип, стан якого багатший за одне булеве значення, відкриває його через
метод status(), що повертає enum, замість геттера is*() —
так зроблено в RecvResult::status() у Channel та в Once::status(): Once\Status
(Uninitialized/Pending/Ready/Poisoned). Використовуйте status(),
коли відповідь має більше ніж два варіанти.
Mutex не відкриває isCorrupted() — пошкодження є стійким,
невідновним і проявляється через CorruptedMutexException під час
наступного захоплення. З такою перевіркою не можна зробити нічого корисного, крім
повторного захоплення й перехоплення винятку.
5. Трихотомія політик очікування — try* / без суфікса / *Timeout
Блокувальні примітиви (Channel, Mutex) виражають політику очікування
через назву методу, а не через перевантажений аргумент ?float $timeout:
| Суфікс | Поведінка | Приклади |
|---|---|---|
try* |
Неблокувальний; одразу повертає варіант невдачі. | Channel::trySend, Channel::tryRecv, Mutex::tryWithLock |
| (без суфікса) | Блокується назавжди (або доки файбер запиту не скасовано). | Channel::send, Channel::recv, Mutex::withLock |
*Timeout |
Обмежене очікування. Приймає обов’язковий int $ms > 0. |
Channel::sendTimeout, Channel::recvTimeout, Mutex::withLockTimeout |
Трихотомія виносить три неоднозначні політики (null = назавжди, 0
= спроба, додатне значення = обмежено) з одного параметра в три
методи із самодокументованими назвами.
Аргумент $ms у методах *Timeout є строго додатним. Нуль,
від’ємні, нецілі та відсутні значення спричиняють OxPHP\Shared\TypeException
на рівні мосту.
Операції з умовним успіхом живуть у Map під назвою setIfAbsent,
а не try*: Map::setIfAbsent фіксує запис лише тоді, коли
ключ був відсутній (аналогічно до HashMap::try_insert), і повертає
наявне значення або null, якщо вставлення відбулося.
Назва setIfAbsent зарезервована саме для цієї семантики; не
використовуйте її деінде.
Те, як метод try* повідомляє про невдачу, залежить від типу: Channel
повертає типізований Result, Mutex кидає ContentionException,
Pool::tryAcquire() повертає null, коли пул насичений, а
Once::trySet() повертає false, коли комірка вже в стані Ready або
Pending.
6. Порівняння з обміном — compareAndSet()
Atomic::compareAndSet(), Flag::compareAndSet(). Завжди повертає
bool (обмін відбувся або ні).
7. Заміна з поверненням попереднього — swap()
Atomic::swap() для int, Flag::swap() для bool. Повертає
попереднє значення.
8. Атомарний RMW з поверненням попереднього — префікс fetch*()
Atomic::fetchAdd(), fetchSub(), fetchAnd(), fetchOr(),
fetchXor().
Префікс fetch кодує контракт повернення: значення до
операції. На противагу цьому Counter::add() повертає нове
значення (агрегований лічильник у стилі LongAdder).
Додаючи нові RMW-методи, спершу оберіть контракт, а потім назву:
- повернення попереднього значення →
fetchVerb(args) - повернення нового значення → просто
verb(args)
Не змішуйте.
9. Скидання до початкового стану — clear()
Map::clear() — спорожнює контейнер; повертає кількість
видалених записів.
Counter не має clear() — його віконним скиданням є set(0).
Counter::set() — задокументований виняток, що повертає
попереднє значення (а не void): це атомарний обмін, а
set(0), що зчитує попередню суму, — це ідіома LongAdder sumThenReset.
(Atomic називає ту саму операцію swap(); Counter зберігає
set, бо set($n) природно читається для початкового заповнення й роботи з вікнами.)
10. Ідентичність у реєстрі — id(): int
Кожен екземпляр Shared\* відкриває id(): int для журналів та
ендпоінта спостережуваності /__ox_shared/entry?id=<id>.
Шпаргалка
| Поняття | Канонічна назва | Приклади |
|---|---|---|
| Читання значення | get() |
Map::get, Counter::get |
| Читання атомарного значення | load($order) |
Atomic::load |
| Запис значення | set() |
Map::set |
| Запис атомарного значення | store($v, $order) |
Atomic::store |
| Кількість елементів | count(): int |
Map::count, Channel::count |
| Булева властивість | is*(): bool |
Channel::isClosed |
| Умовна вставка | setIfAbsent($k, $v) |
Map::setIfAbsent |
| Неблокувальне очікування | try*() |
Channel::trySend, Mutex::tryWithLock |
| Безстрокове очікування | дієслово без суфікса | Channel::send, Channel::recv, Mutex::withLock |
| Обмежене очікування | *Timeout(int $ms) |
Channel::sendTimeout, Mutex::withLockTimeout |
| Порівняння з обміном | compareAndSet() |
Atomic::compareAndSet |
| Обмін, повертає попереднє | swap() |
Atomic::swap, Flag::swap |
| Атомарний RMW, повертає попереднє | fetch*() |
Atomic::fetchAdd |
| Атомарний RMW, повертає нове | дієслово без суфікса | Counter::add |
| Скидання до початкового стану | clear() |
Map::clear |
| Ідентифікатор у реєстрі | id(): int |
кожен тип Shared\* |
Додавання нового типу Shared\*
Пропонуючи новий примітив, заповніть цей чек-лист перед злиттям:
- Кожен метод відповідає рядку шпаргалки або має ADR,
що пояснює виняток (див.
Atomic::load/storeтаCounter::setвище). - Якщо тип містить колекцію значень, він реалізує
\Countableі відкриваєcount(): int. - Методи читання — це
getабоload(лише для атомарних). - Булеві геттери використовують префікс
is*. - Варіанти політики очікування дотримуються трихотомії
try*/ без суфікса /*Timeout(int $ms). Варіант*Timeoutприймаєint $ms > 0і відхиляє нульове / від’ємне / нецілочислове значення зTypeException. Методиtry*політики очікування або повертають типізований Result, або кидають предметний виняток — ніколи не кодують невдачу черезnull.Pool::tryAcquire(), який повертаєnull, коли пул насичений, цьому не відповідає. Операції з умовним успіхом дотримуються окремого іменуванняsetIfAbsentзамістьtry*. - Жодних
len,size,pending,testчи інших довільних назв. - Предметно-специфічні дієслова (
evict,drain,flushтощо) з’являються лише тоді, коли жоден канонічний запис шпаргалки не покриває поняття.
Назви спостережуваності
Інтерфейс для операторів — назви метрик Prometheus і JSON за адресою
/__ox_shared/entry?id=<id> — є окремим контрактом, незалежним від PHP API,
бо їхнє перейменування ламає дашборди й правила алертів. Кількість
елементів Channel чи Pool — це oxphp_shared_channel_count або
oxphp_shared_pool_count на /metrics і count у його JSON
type_specific. Для Channel це число, яке повертає Channel::count();
для Pool — живі слоти, тобто число, яке повертає Pool::stats()->size().
Попередні назви — oxphp_shared_channel_pending,
oxphp_shared_pool_size, а також ключі JSON pending і size — більше
не публікуються. Дашборди й правила алертів, досі написані під них, не
знаходять жодних рядів, тож порогові алерти на них ніколи не спрацюють.
Стабільність
Ці правила є частиною контракту OxPHP\Shared\* 1.0. Після
релізу 1.0 перейменування є несумісними змінами й потребують циклу
застарівання. До 1.0 правила так само обов’язкові — нові методи, що
їх порушують, буде відхилено під час рев’ю.