giip
SES案件登録
8分で読了

MQE 技術仕様(tMQLog ベースの通知キュー)

MQE(Message Queue Engine)は、SQL Server のテーブル tMQLog をキューとして使う GIIP の通知送信エンジンです。本書ではテーブルスキーマ、ストアドプロシージャのシグネチャ、送信パイプライン、そして実装者が必ず把握すべき制約を記述します。

🔔 MQE 通知設定画面へ移動 →

⚠️ はじめに — アーキテクチャの誤解を正す

MQE に 外部メッセージブローカーは存在しません。 専用キュープロトコル、別途の通信ポート、失敗メッセージの隔離キュー、メッセージ有効期限といった概念も GIIP には実装されていません。実際の構造は次のとおりです。

構成要素実体
キューSQL Server テーブル tMQLog
プロデューサーストアドプロシージャ pApiMQLogPutbyAk / pApiMQLogPutbySk(HTTP または T-SQL から呼び出し)
コンシューマー送信ワーカー(execmqe)。pApiMQListbySk で未送信一覧を読み、pApiMQUpdatebySk で状態を更新
送信先設定テーブル tMQENotificationConfig/ja/mq-config 画面 で管理)
配信チャンネルメール(SMTP)、Slack API

🔁 パイプライン

🗄️ tMQLog スキーマ

#NULL説明
1mqSnbigint IDENTITYN主キー。メッセージ通番。API 応答の mqSn はこの値です。
2mqRegdtdatetimeY登録時刻。SP が GETDATE() を設定します。
3mqSchdtdatetimeY送信予定時刻。SP が GETDATE() を設定します(即時送信)。
4mqSentdtdatetimeY送信完了時刻。NULL なら未送信です。
5ktSnbigintYKVS トリガー通番(tKVSTrigger)。トリガー由来の通知でのみ使用。
6clSnbigintYKVS チェックログ通番(tKVSChkLog)。
7cSnbigintY顧客番号。SK から導出されます。
8mqTypevarchar(16)Y配信チャンネル。既定値 EMAIL
9mqTonvarchar(200)Y送信先。SLACK ならチャンネル ID、EMAIL ならメールアドレス。
10mqSubjectnvarchar(500)Y件名。重複防止ゲートの判定キーの一つ。
11mqBodynvarchar(max)Y本文。
12mqFromEmailvarchar(255)Y送信元メールアドレス。
13mqFromNamenvarchar(100)Y送信者表示名。
14mqErrMsgnvarchar(1000)Y直近の送信失敗理由。
15mqRetryCnttinyintY試行回数。初期値 0、上限 3。
16mqPrioritytinyintY優先度。SP は常に 2 を挿入します。小さいほど先に送信。
17mcSnintYメールコンテンツ通番(tMQEMailContents)。
18mqAiAnalyzedbitYMQE AI 分析済みフラグ。初期値 0。

📥 プロデューサー SP

pApiMQLogPutbyAk — JSON 単一パラメータ方式(推奨)

pApiMQLogPutbyAk
    @ak       VARCHAR(200),    -- 実際は SK。tSecretKey.skey と突き合わせます。
    @jsondata NVARCHAR(MAX)    -- メッセージ内容の JSON

@jsondata のキー:

キー必須対象列既定値
mqSubject必須mqSubject
mqBody必須mqBody
mqTo任意mqToNULL
mqType任意mqType'EMAIL'
mqFromEmail任意mqFromEmailNULL
mqFromName任意mqFromNameNULL

SP が固定で書き込む値: mqRegdt = GETDATE()mqSchdt = GETDATE()mqSentdt = NULLmqRetryCnt = 0mqPriority = 2mqAiAnalyzed = 0

戻り値は常に 1 行 3 列です。

RstVal (int) | RstMsg (nvarchar) | mqSn (bigint)

HTTP 経路:

POST https://giipfaw.azurewebsites.net/api/giipApiJson
Content-Type: application/x-www-form-urlencoded; charset=utf-8

usertoken=<SK>&text=MQLogPut&jsondata=<URL エンコードした JSON>

Azure Function はこのリクエストを exec pApiMQLogPutbyAK N'<usertoken>', N'<jsondata>' に変換して実行します。

pApiMQLogPutbySk — 個別パラメータ方式

pApiMQLogPutbySk
    @sk          NVARCHAR(100),
    @mqTo        NVARCHAR(200),
    @mqSubject   NVARCHAR(500),
    @mqBody      NVARCHAR(MAX),
    @mqType      VARCHAR(16)   = 'EMAIL',
    @mqFromEmail VARCHAR(255)  = NULL,
    @mqFromName  NVARCHAR(100) = NULL

動作は pApiMQLogPutbyAk と同じですが、@mqTo も必須 です(NULL なら RstVal=400)。HTTP 経路は giipApiSk2 で、text に SP 名とパラメータ名を並べます。

POST https://giipfaw.azurewebsites.net/api/giipApiSk2
Content-Type: application/x-www-form-urlencoded; charset=utf-8

token=<SK>&text=MQLogPut mqTo mqSubject mqBody mqType&jsondata=<URL エンコードした JSON>

mqTo を省略したい場合は pApiMQLogPutbyAk(giipApiJson)を使ってください。

🔐 認証と cSn の決定規則

どちらの SP も次の 1 文で顧客を決定します。

SELECT @cSn = csn FROM tSecretKey WITH(NOLOCK) WHERE skey = @ak;   -- PutbySk は @sk
  • パラメータ名は @ak / usertoken ですが、AK ではなく SK を渡します。 SP のコメントにも treating @ak as sk for this context と明記されています。
  • cSn を直接指定するパラメータはありません。 対象顧客を変えるには、その顧客の SK を使う必要があります。
  • 一致する SK が無い場合 @cSn は NULL になり、そのメッセージはどの送信先設定とも結び付かないため送信されません。

🛑 重複防止ゲート

INSERT の直前に次の検査が実行されます。

IF EXISTS (
    SELECT 1 FROM tMQLog WITH(NOLOCK)
    WHERE mqSentdt IS NULL
      AND mqSubject = @mqSubject
      AND mqTo      = @mqTo
      AND cSn       = @cSn
)
BEGIN
    SELECT 200 AS RstVal, N'Skipped: Identical unsent message already exists' AS RstMsg, 0 AS mqSn;
    RETURN;
END

設計上重要な性質:

  1. 判定キーは (mqSubject, mqTo, cSn) の 3 つ で、本文(mqBody)は見ません。件名が同じなら本文が違っても破棄されます。
  2. スキップは失敗ではなく成功(RstVal=200)として応答 されるため、呼び出し側は必ず mqSn を確認する必要があります。
  3. 判定対象が 未送信メッセージ であるため、送信先設定の欠落などで送信できなかったメッセージが残っていると その件名は永久に塞がれます。

実装指針:

  • 定期送信されるレポートは件名に日付または時刻を含めます(... - 2026-09-16)。
  • 応答の mqSn が 0 なら送信失敗とみなしてログに残します。

📤 コンシューマー SP

pApiMQListbySk — 送信待ち一覧の取得

pApiMQListbySk @ak VARCHAR(64)   -- SK

抽出条件(WHERE 句)は次のとおりです。

WHERE ml.mqSentdt IS NULL
  AND ISNULL(ml.mqSchdt, DATEADD(SECOND, -1, GETDATE())) < GETDATE()
  AND (ml.mqTo IS NOT NULL OR kt.kMailAct = 1 OR mnc.mncSn IS NOT NULL)
ORDER BY ml.mqPriority ASC, ml.mqSchdt ASC

3 番目の条件が重要です。mqTo が NULL の場合、同じ cSn・同じ mqType の有効な tMQENotificationConfig が存在するときにのみ 一覧に含まれます。そうでなければメッセージは保存されたまま永久に待機します。

この SP は次の条件で設定テーブルを結合し、Slack の資格情報も併せて返します。

LEFT JOIN tMQENotificationConfig mnc WITH(NOLOCK)
  ON ml.cSn = mnc.csn AND ml.mqType = mnc.mqType AND mnc.mncIsActive = 1

DB の照合順序が SQL_Latin1_General_CP1_CI_AS(大文字小文字を区別しない)であるため、mqTypeSLACKslack は同一として一致します。

pApiMQUpdatebySk — 送信結果の反映

pApiMQUpdatebySk
    @ak       VARCHAR(64),          -- SK
    @mqSn     BIGINT,               -- 対象メッセージ
    @mqErrMsg NVARCHAR(1000) = NULL -- 失敗時の理由
呼び出し形態動作
@mqErrMsg が NULLmqSentdt = GETDATE()mqErrMsg = NULLmqRetryCnt + 1 → 送信完了
@mqErrMsg あり、mqRetryCnt < 3mqErrMsg を記録し mqRetryCnt + 1mqSentdt は NULL のままなので 次のバッチが再取得します。
@mqErrMsg あり、mqRetryCnt >= 3リトライ上限に到達
存在しない @mqSnRstVal=404

リトライ上限は SP 内部に @maxRetries = 3 として固定されています。設定で変更することはできません。

⚙️ 送信先設定テーブル tMQENotificationConfig

NULL説明
mncSnintN主キー
csnintN顧客番号
mqTypevarchar(16)Nemail または slack
mncEmailTonvarchar(1000)Y受信者。カンマ区切りで複数指定可
mncEmailCcnvarchar(1000)YCC
mncEmailBccnvarchar(1000)YBCC
mncSlackBotTokenvarchar(255)YSlack Bot User OAuth Token(xoxb-
mncSlackChannelIdvarchar(100)Yチャンネル ID
mncSlackChannelNamenvarchar(200)Yチャンネル表示名(参考用)
mncIsActivebitN有効フラグ。0 なら結合対象から外れます
mncPrioritytinyintN優先度(1〜10)
mncDescriptionnvarchar(1000)Y説明
mncCreatedAt / mncUpdatedAtdatetimeN/Y作成・更新時刻
mncCreatedBy / mncUpdatedByintY作成者・更新者 uSn

管理用 SP は pApiMQEConfigListbyAKpApiMQEConfigPutbyAKpApiMQEConfigDeletebyAKpApiMQEConfigTestbyAK の 4 つで、/ja/mq-config 画面giipApiSk2 経由でこれらを呼び出します。

🧩 その他の MQE 資産

オブジェクト用途
tMQEAdvReport + pApiMQEAdvResultPutbySkMQE AI 分析結果の保存。@mqSn が必要なため tMQLog の行に紐づく構造であり、単独の登録経路ではありません。
pApiMQEHealthCheckReportBySKpApiMQEHealthCheckReportSingleBySKtMQEHealthCheckRulesヘルスチェック結果を tMQLog に投入
tMQEMailFormLayouttMQEMailContentspApiMQEMailFormLayoutGetbySkメール HTML テンプレート
pAutoKVSLtoMQLpMQETrgUpdateAutoKVS トリガー由来の自動通知を投入
tMQCmdLogpMQCmdListコマンドログ

📊 運用実測値(2026-09-16)

  • 登録から送信までの所要時間: 最短 2 秒、最長で約 7 分。送信ワーカーが数分間隔のバッチで動作するためです。
  • 未送信のまま長期滞留しているメッセージが存在します(2026-02-05 登録、mqTo NULL、当該 mqType の有効な送信先設定なし)。上記「重複防止ゲート」3 番の実例です。
  • リトライ上限に達したメッセージの例: mqRetryCnt = 3mqErrMsg = account_inactive(Slack アカウント無効)。

🌏 非 ASCII 文字の取り扱い

件名・本文に日本語 / 韓国語 / 中国語が含まれる場合:

  • T-SQL 直接呼び出し: 文字列リテラルに必ず N を付けます(@jsondata = N'{...}')。省くと ? で保存されます。
  • HTTP 呼び出し: リクエストボディを UTF-8 ファイルに保存し curl --data-binary @ファイル で送ります。非 ASCII 文字をシェル引数で直接渡すと、Windows 環境ではコードページ変換により ? となり、そのまま DB に保存されることを実測しました(2026-09-16)。

送信ワーカーには mojibake ガード があります。本文に ? が連続していると送信を拒否し、mqErrMsgMOJIBAKE: body contains a run of N consecutive '?' ... (giip #2462) を記録してリトライ上限を使い切ります。 つまり 壊れたメッセージは送信されずキューの中で死にます。 実測(2026-09-16): シェル引数で送った 2 件は ガードに阻まれて未送信、ファイル経由で送った 1 件は 36 秒後に正常送信されました。

🔬 動作検証の方法

副作用なしで SP の動作を確認するには、トランザクション内で実行してロールバックします。

BEGIN TRAN;

EXEC dbo.pApiMQLogPutbyAk
     @ak       = 'YOUR_SECRET_KEY',
     @jsondata = N'{"mqTo":"ops@example.com","mqSubject":"[verify] 2026-09-16 12:00","mqBody":"verification","mqType":"EMAIL"}';
-- 期待: RstVal=200, RstMsg=Success, mqSn>0

EXEC dbo.pApiMQLogPutbyAk
     @ak       = 'YOUR_SECRET_KEY',
     @jsondata = N'{"mqTo":"ops@example.com","mqSubject":"[verify] 2026-09-16 12:00","mqBody":"second call","mqType":"EMAIL"}';
-- 期待: RstVal=200, RstMsg=Skipped: Identical unsent message already exists, mqSn=0

ROLLBACK;

🔗 関連文書


バージョン: 2.0 最終更新: 2026-09-16 ソースファイル: giipv3/public/help/mqe-spec.ja.md