0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Ninda 9日目: イベント通知 〜リアクティブなタプルスペース〜

Posted at

今日のゴール

  • イベント駆動プログラミングの概念を理解する
  • nindaのサブスクリプションシステムを使いこなす
  • リアクティブなアプリケーションを構築する

ポーリング vs イベント駆動

ポーリングの問題点

# ❌ ポーリング: 非効率
while true:
  let result = await ts.tryReadAsync(pattern)
  if result.isSome:
    break
  await sleepAsync(100)  # CPUを使い続ける
問題 影響
CPU時間の無駄 バッテリー消費、コスト増
遅延 ポーリング間隔分の反応遅れ
スケール困難 監視対象が増えると負荷も増加

イベント駆動の利点

# ✅ イベント駆動: 効率的
let sub = await ts.subscribeAsync(pattern, {evWrite})
while sub.isActive:
  let event = await sub.recvEvent()
  # 変更があったときだけここに来る

イベントタイプ

nindaは3種類のイベントを提供します:

イベント 発生条件 ユースケース
evWrite タプルが書き込まれた 新規データの監視
evTake タプルが取り出された 消費の追跡
evExpire タプルが期限切れ TTLベースのクリーンアップ

基本的な使い方

# サブスクリプション作成
let sub = await ts.subscribeAsync(
  toPattern(strVal("order"), nilValue()),  # パターン
  {evWrite, evTake}                        # 監視するイベント
)

# イベント受信
let event = await sub.recvEvent()
echo event.eventType  # evWrite or evTake
echo event.data       # 対象のタプル

# 購読解除(必須!)
sub.unsubscribe()

パターンでフィルタリング

パターンを使って、関心のあるタプルだけを監視できます。

# "order"タプルのみを購読
let orderSub = await ts.subscribeAsync(
  toPattern(strVal("order"), nilValue(), nilValue()),
  {evWrite}
)

# "log"タプルのみを購読
let logSub = await ts.subscribeAsync(
  toPattern(strVal("log"), nilValue()),
  {evWrite}
)

# "user"タプルは誰も受信しない

ブロッキング vs 非ブロッキング

メソッド 動作 用途
recvEvent() イベントが来るまで待機 専用の監視ループ
tryRecvEvent() 即座にNone or Someを返す 他処理と並行
# 非ブロッキング: イベントループ内で使用
while running:
  let event = sub.tryRecvEvent()
  if event.isSome:
    processEvent(event.get())
  # 他の処理も実行
  await doOtherWork()

実践例: リアルタイム監視

# 監視プロセス
proc monitor() {.async.} =
  let sub = await ts.subscribeAsync(
    toPattern(strVal("metric"), nilValue(), nilValue()),
    {evWrite}
  )

  while sub.isActive:
    let event = await sub.recvEvent()
    let name = event.data[1].strVal
    let value = event.data[2].intVal

    # しきい値チェック
    if name == "cpu" and value > 80:
      echo "[ALERT] High CPU: ", value, "%"

ポイント:

  • evWriteを監視してリアルタイム検知
  • パターンでmetricタプルだけをフィルタ
  • 即座にアラート発生

ベストプラクティス

1. 必ずunsubscribeする

let sub = await ts.subscribeAsync(pattern, {evWrite})
try:
  # イベント処理
  while sub.isActive:
    let event = await sub.recvEvent()
    processEvent(event)
finally:
  sub.unsubscribe()  # リソースリーク防止

2. isActiveをチェック

while sub.isActive:  # unsubscribe後はfalseになる
  let event = await sub.recvEvent()

3. バッファサイズを適切に設定

# 高頻度イベント用
let highFreqSub = await ts.subscribeAsync(pattern, {evWrite}, bufferSize = 1000)

# 低頻度イベント用(メモリ節約)
let lowFreqSub = await ts.subscribeAsync(pattern, {evWrite}, bufferSize = 10)

💡 バッファが溢れると古いイベントが破棄されます。想定イベント頻度に合わせて設定しましょう。


まとめ

学んだこと

トピック ポイント
サブスクリプション subscribeAsyncでイベントを購読
イベントタイプ evWrite, evTake, evExpire
パターンフィルタ 関心のあるタプルだけを監視
リソース管理 必ずunsubscribe()を呼ぶ

いつ使うか

  • リアルタイム監視: メトリクス、ログ
  • 変更通知: データ更新の伝播
  • イベントソーシング: 状態変更の追跡

演習問題

問題9-1: チャットシステム

メッセージタプルの書き込みを監視して、リアルタイムにメッセージを表示するシステムを作成してください。

問題9-2: 在庫アラート

在庫数が10以下になったときにアラートを出すシステムを、イベント駆動で実装してください。

問題9-3: イベント集計

5秒間に発生したイベント数をカウントして、1秒ごとに表示するプログラムを書いてください。

💡 完全な実装例は GitHubリポジトリ を参照してください。


前回: タイムアウト処理 | 目次 | 次回: 有効期限(TTL)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?