コマンドディスパッチ

コマンドディスパッチは、プロセスの yield をハンドラへルーティングし、相関付けられた結果をプロセスイベントキュー経由で返します。

これは拡張および実装リファレンスです。カスタムコマンドとディスパッチャーの断片は、既存の Go パッケージ、ブートグラフ、コマンド API、およびサービス固有のエラー処理を前提としています。

フロー

sequenceDiagram
    participant P as Process
    participant W as Worker
    participant R as Registry
    participant H as Handler

    P->>W: yield(command, tag)
    W->>R: getHandler(cmdID)
    R-->>W: handler
    W->>H: Handle(cmd, tag, receiver)
    H-->>H: async work
    H->>W: CompleteYield(tag, result)
    W->>P: queue event, wake
    P->>P: resume with result

コマンドレジストリ

レジストリはハンドラをハイブリッド構造で格納します。

type Registry struct {
    handlers [256]Handler         // System commands: O(1) index
    extended map[CommandID]Handler // Extended commands: map lookup
    frozen   atomic.Bool          // Lock-free after boot
}

システムコマンド(0~255)は配列インデックスを使用します。拡張コマンドはマップ検索を使用します。Freeze() 後は、すべての検索がロックフリーになります。

コマンド ID の範囲

範囲 モジュール 例
1-9 process Send, Spawn, Terminate, Cancel, Monitor, Unmonitor, Link, Unlink, Exec
10-29 clock Sleep, Ticker, Timer
30-39 socket Dial, Listen, Accept, Close
50-59 stream Read, Write, Close, Seek
60-69 http Request, RequestBatch
70-79 tty ターミナルI/O
80-89 websocket Connect, Send, Receive
90-99 event Subscribe, Send
100-119 sql Query, Execute, Prepare, Stmt, Tx ops
120-129 store Get, Set, Delete, Has
130-139 security ValidateToken, CreateToken
140-149 function Call, AsyncStart, AsyncCancel
150-159 exec ProcessWait
160-169, 173-174 cloudstorage Upload, Download, List, Presigned URLs, Multipart, OpenReader
170-171 eval Compile, Run
172 cdc Subscribe
180-189 workflow SideEffect, Exec, Version, UpsertAttrs
190-199 contract Open, Call, AsyncCall, AsyncCancel
200-211 pg (プロセスグループ) Join, Leave, GetMembers, GetLocalMembers, WhichGroups, Broadcast, BroadcastLocal, WhichLocalGroups, Monitor, Events, JoinGroups, LeaveGroups
256+ custom ユーザー定義サービス

パッケージは init() から MustRegisterCommands() を使用してコマンド ID の所有権を予約します。所有権の衝突は、パッケージの初期化中に panic を発生させます。コンポーネントのロード中、各サービスは Registrar.Register を通じてハンドラをバインドします。ディスパッチャーが freeze されるのは、それらのハンドラがインストールされた後です。

コマンドの定義

コマンドは一意の CommandID を持つデータ構造です。

const MyCommand dispatcher.CommandID = 256

type MyCmd struct {
    Input  string
    Option int
}

func (c *MyCmd) CmdID() dispatcher.CommandID { return MyCommand }

パッケージ初期化時にコマンド ID を予約します。

func init() {
    dispatcher.MustRegisterCommands("myservice", MyCommand)
}

ディスパッチャー

ディスパッチャーは関連するハンドラをグループ化します。ハンドラを登録する RegisterAll と、セットアップ/ティアダウン用のライフサイクルメソッドを実装します。

type Handler interface {
    Handle(ctx context.Context, cmd Command, tag uint64, receiver ResultReceiver) error
}

type ResultReceiver interface {
    CompleteYield(tag uint64, data any, err error)
}
type Dispatcher struct {
    // service state
}

func (d *Dispatcher) RegisterAll(register func(id dispatcher.CommandID, h dispatcher.Handler)) {
    register(myapi.MyCommand, dispatcher.HandlerFunc(d.handleMyCommand))
}

func (d *Dispatcher) handleMyCommand(ctx context.Context, cmd Command, tag uint64, receiver ResultReceiver) error {
    c := cmd.(*myapi.MyCmd)
    go func() {
        result := doWork(c)
        if ctx.Err() == nil {
            receiver.CompleteYield(tag, result, nil)
        }
    }()
    return nil
}

ブートコンポーネントとして登録します。

func MyDispatcher() boot.Component {
    return boot.New(boot.P{
        Name:      "dispatcher.myservice",
        DependsOn: []boot.Name{DispatcherName},
        Load: func(ctx context.Context) (context.Context, error) {
            reg := dispatcher.GetRegistrar(ctx)
            svc := myservice.NewDispatcher()
            svc.RegisterAll(reg.Register)
            return ctx, nil
        },
    })
}

Yield と相関

プロセスが非同期処理を必要とする場合、相関タグ付きのコマンドを yield します。

type Yield struct {
    Cmd Command
    Tag uint64    // Process-local counter for correlation
}

ワーカーは各ステップの後に StepOutput から yield を取り出し、ハンドラへディスパッチします。各タグはリクエストを一意に識別するため、結果を対応付けて戻すことができます。

関連項目