Соглашения об именовании 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 = null) — намеренное исключение:
его наличие несёт аргумент упорядочивания, сигнализируя, что чтение
является частью контракта модели памяти и отличается от обычного геттера.
2. Запись значения — set(), store() для атомиков
Map::set(), сброс значения Mutex (через with), Once::getOrInit().
Atomic::store($value, ?Ordering) повторяет load по той же причине.
3. Число элементов — count(): int
Каждый контейнер, раскрывающий свой текущий размер, делает это под именем
count(): int. Channel дополнительно реализует \Countable, поэтому
count($ch) работает как нативная идиома для элементов в очереди. Map и
Pool предоставляют count(): int как метод, но не реализуют
\Countable — вызывайте его напрямую:
$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->count(); // 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 строго положителен. Ноль,
отрицательные, не-int и отсутствующие значения вызывают OxPHP\Shared\TypeException
на мосту.
Операции с условным успехом располагаются на Map под написанием setIfAbsent,
а не try*: Map::setIfAbsent фиксирует изменение только когда
ключ отсутствовал, и возвращает bool (параллель к HashMap::try_insert).
Имя setIfAbsent зарезервировано за этой единственной семантикой; не
переиспользуйте его в других местах.
Объединяющий инвариант для try*: он либо возвращает типизированный по значению
Result (Channel), либо бросает ContentionException (Mutex). Он никогда
не возвращает null, чтобы закодировать «не удалось». Так работал старый API, и это
порождало неоднозначность с оператором null-объединения, которую устраняет трихотомия.
6. Сравнение с обменом (compare-and-swap) — 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() — опустошает контейнер; возвращает void.
У Counter нет clear() — set(0) служит его оконным сбросом.
Counter::set() — задокументированное исключение, которое возвращает
предыдущее значение (а не void): это атомарный обмен, и
set(0), читающий прежнюю сумму, — это идиома sumThenReset из LongAdder.
(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, Pool::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и отвергает ноль / отрицательные / не-int значения сTypeException. Методы политики ожиданияtry*возвращают либо типизированный по значению Result, либо бросают доменное исключение — но никогда не кодируют результат черезnull. Операции с условным успехом следуют выделенному именованиюsetIfAbsentвместоtry*. - Никаких
len,size,pending,testили других произвольных имён. - Предметно-специфичные глаголы (
evict,drain,flushи т. д.) появляются только тогда, когда ни одна каноническая запись в шпаргалке не покрывает концепцию.
Имена в наблюдаемости отстают от PHP API
Интерфейс для оператора — имена метрик Prometheus и JSON по адресу
/__ox_shared/entry?id=<id> — это отдельный от PHP API контракт.
Его переименование ломает дашборды и правила алертов. Чтобы избежать скрытого
рассогласования, затронутые имена выдаются дважды в течение одного
цикла релиза:
| Интерфейс | Устаревшее (всё ещё выдаётся) | Каноническое |
|---|---|---|
| Prometheus | oxphp_shared_channel_pending |
oxphp_shared_channel_count |
| Prometheus | oxphp_shared_pool_size |
oxphp_shared_pool_count |
| Запись JSON | Channel.pending |
Channel.count |
| Запись JSON | Pool.size |
Pool.count |
Строки # HELP устаревших метрик содержат префикс (deprecated, removed in a future release; use *_count), а плагин ox_shared
выдаёт при старте WARN, когда включена интроспекция или метрики.
Переведите дашборды и правила алертов на имена с _count до
закрытия цикла устаревания. После удаления будут выдаваться только
канонические имена, и панели Prometheus/Grafana, ссылающиеся на старые,
начнут возвращать пустые серии.
Стабильность
Эти правила — часть контракта OxPHP\Shared\* 1.0. После
релиза 1.0 переименования являются ломающими изменениями и требуют цикла
устаревания. До 1.0 правила всё равно обязательны — новые методы,
которые их нарушают, будут отклонены на ревью.