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) は意図的な例外です。ordering 引数を
伴って存在すること自体が、この読み取りが単なるゲッターとは異なるメモリモデル
契約の一部であることを示しています。
2. 値を書く — set()、atomic には 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 プレフィックスは、真偽値プロパティの純粋な読み取りであることを示します。
状態が単一の真偽値よりも豊かな型は、それを is*() ゲッターではなく、
enum を返す status() メソッドとして公開します。Channel の
RecvResult::status() と Once::status(): Once\Status
(Uninitialized/Pending/Ready/Poisoned)がこれに従っています。答えが
2 つより多くのケースを持つ場合は status() を使ってください。
Mutex は isCorrupted() を公開しません。破損は解消不能で残り続け、
次の acquire の際に CorruptedMutexException として表面化します。
再取得して catch する以外に、そのプローブでできる有用なことはありません。
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 |
この三分法は、3 つのあいまいなポリシー(null = 永久、0 = try、正の値
= 有界)を 1 つのパラメータから取り出し、自己文書化された名前を持つ 3 つの
メソッドに分けます。
*Timeout メソッドの $ms 引数は厳密に正です。ゼロ、負数、非整数、
欠落した値はブリッジで OxPHP\Shared\TypeException を送出します。
条件付き成功の操作は、try* ではなく setIfAbsent という綴りで Map 上に
存在します。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()
int には Atomic::swap()、bool には Flag::swap()。前の値を返します。
8. 前の値を返す atomic 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) が直前の合計を読み取ることは
LongAdder の sumThenReset イディオムです。(Atomic は同じ操作を swap()
と綴りますが、Counter は set を維持します。set($n) はシード設定と
ウィンドウ処理において自然に読めるためです。)
10. レジストリ ID — id(): int
すべての Shared\* インスタンスは、ログおよび
/__ox_shared/entry?id=<id> 可観測性エンドポイントのために id(): int
を公開します。
チートシート
| 概念 | 正規の名前 | 例 |
|---|---|---|
| 値を読む | get() |
Map::get、Counter::get |
| atomic を読む | load($order) |
Atomic::load |
| 値を書く | set() |
Map::set |
| atomic を書く | 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 |
| Compare-and-swap | compareAndSet() |
Atomic::compareAndSet |
| スワップして前の値を返す | swap() |
Atomic::swap、Flag::swap |
| atomic RMW、前の値を返す | fetch*() |
Atomic::fetchAdd |
| atomic RMW、新しい値を返す | 裸の動詞 | Counter::add |
| デフォルトへリセット | clear() |
Map::clear |
| レジストリ ID | id(): int |
すべての Shared\* 型 |
新しい Shared\* 型の追加
新しいプリミティブを提案する際は、マージ前にこのチェックリストを埋めて ください。
- すべてのメソッドがチートシートの行にマッピングされる、または例外を
説明する ADR がある(上記の
Atomic::load/storeとCounter::setを 参照)。 - 型が値のコレクションを保持する場合、
\Countableを実装しcount(): intを公開する。 - 読み取りメソッドは
getまたはload(atomic のみ)である。 - 真偽値ゲッターは
is*プレフィックスを使う。 - 待機ポリシーのバリアントは
try*/ 裸 /*Timeout(int $ms)の 三分法に従う。*Timeoutバリアントはint $ms > 0を取り、ゼロ / 負数 / 非整数の入力をTypeExceptionで拒否する。待機ポリシーのtry*メソッドは値型の Result を返すか、ドメイン例外を送出するかのいずれかで あり、nullでのエンコードは決して行わない。条件付き成功の操作はtry*ではなく専用のsetIfAbsent命名に従う。 -
len、size、pending、test、その他の場当たり的な名前は使わない。 - ドメイン固有の動詞(
evict、drain、flushなど)は、チートシートの 正規エントリがその概念をカバーしていない場合にのみ登場する。
可観測性の名前は PHP API より遅れる
オペレーター向けのサーフェス(Prometheus メトリクス名と
/__ox_shared/entry?id=<id> の JSON)は、PHP API とは別の契約です。
これをリネームするとダッシュボードとアラートルールが壊れます。暗黙の
不整合を避けるため、影響を受ける名前は 1 リリースサイクルの間二重に
出力されます。
| サーフェス | 非推奨(まだ出力される) | 正規 |
|---|---|---|
| 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 プラグインは
introspection またはメトリクスが有効なときに起動時 WARN を出力します。
非推奨サイクルが終了する前に、ダッシュボードとアラートルールを _count
名へ移行してください。削除後は正規の名前だけが出力され、古い名前を参照する
Prometheus/Grafana のパネルは空のシリーズを返し始めます。
安定性
これらのルールは OxPHP\Shared\* 1.0 契約の一部です。1.0 リリース後は、
リネームは破壊的変更であり、非推奨サイクルを必要とします。1.0 より前でも、
これらのルールは依然として拘束力を持ちます。これらに違反する新しいメソッドは
レビューで却下されます。