← 全記事
ガイド

Telegram getUpdates の offset:重複処理と更新の取りこぼしを防ぐ

Telegram は、ある更新の update_id より大きい offset を指定して getUpdates を呼び出すと、その更新を確認済みにします。レスポンスの受信自体は確認ではありません。取りこぼしを防ぐには、返された更新を永続化してから、大きい offset で次のリクエストを送ります。再起動に備え、次の offset も同時に保存し、重複排除と業務処理は別々に管理してください。

要点

  • offset は確認の境界であり、ページ番号やメッセージ数ではありません。
  • 確認済みになる更新をすべて安全に保存してから進めます。
  • Bot ごとに稼働中のポーリング担当を一つにし、並列化は後段のワーカーで行います。
  • 永続受信箱は受信処理を守りますが、外部への操作には独自の再試行設計が必要です。

この記事は、ポーリングを採用済みの構成を対象にします。Webhook がまだ設定されている場合は、先に getUpdates と setWebhook の切り替え手順を確認してください。ここで扱うのは受信方式の選択ではなく、成功したポーリングと次のリクエストの間で何をコミットするかです。

getUpdates の offset は何を確認するのか

Telegram 公式 Bot API リファレンスでは、offset は最初に返す更新の識別子と定義されています。省略すると、最も古い未確認の更新から返されます。後続の呼び出しで識別子を超える offset を指定すると、その更新が確認済みになります。

**仮の例:**レスポンスに 810081018102 が含まれているとします。次に offset=8103 で呼び出すと、アプリが最後の一件しか処理していなくても、三件とも確認済みになります。Telegram はデータベースの内容や CRM の処理完了を確認しません。

手軽に見える実装問題より安全な設計
バッチ保存前に最大 ID を記録する再起動後、未保存の更新を飛ばす可能性受信箱と次の offset を同時にコミット
最も速い並列タスクの完了時に進める先行する未完了の更新も確認されるワーカーの完了順ではなく永続化の進捗で判断
offset をメモリだけに保持する再起動でローカルのチェックポイントが消えるBot ごとの永続チェックポイントを読み込む
負の offset で重複を解消するキューの古い更新が破棄されるポーリングの担当と重複排除を点検

Telegram は、負の offset がキューの末尾から更新を取得し、それ以前の更新を忘れることを明記しています。必要な本番データの復旧手段ではありません。

受信の進捗と業務の完了を分ける

アプリ側には、完全な更新を格納する受信箱と、次の offset を格納するチェックポイントを用意します。いずれもローカルの保存設計であり、Telegram の追加 API フィールドではありません。

受信箱の一意キーには、Bot の安定した識別情報と update_id を組み合わせます。Bot token 自体をデータベースキーにしたり、ログへ出したりしないでください。現在のワーカーが理解できない種類も含め、返された更新をすべて保存します。分類は受信後に行えます。

推奨するトランザクションの流れ:

この Bot の保存済み next offset を読む。
その offset で getUpdates を呼び出す。
空のバッチならチェックポイントを変えない。
それ以外はデータベーストランザクションを開始する:
  各更新を挿入し、既存の一意キーは重複挿入しない。
  バッチ内の max(update_id) + 1 を次の offset として保存する。
コミットする。
コミット成功後にだけ次のポーリングを行う。
別のワーカーで保存済みの更新を処理する。

これは設計用の疑似コードで、完全なクライアントではありません。単一の稼働ポーラー、永続的なトランザクションストア、一意制約が前提です。トランザクションが失敗したら進捗を進めず、保存済みチェックポイントから再試行します。保存エラーを捕捉して、大きい offset のまま続行してはいけません。

受信箱とチェックポイントが別システムにある場合、この原子性は自動では得られません。二回の書き込み成功を一つのトランザクションとみなさず、永続的な引き渡しと照合の手順を設計してください。

クラッシュの境界をテストする

以下は推奨する受け入れテストであり、実測結果ではありません。

中断箇所期待する復旧動作
バッチ受信後、コミット前古いチェックポイントを読み、再受信を許容
トランザクション中ロールバック後にチェックポイントだけが残らない
コミット後、次のポーリング前新しいチェックポイントを読み、保存済みタスクを継続
外部操作後、完了記録前下流の冪等性または照合で対応。受信キーだけでは操作の重複を防げない

重複した更新は既存の受信箱レコードに対応付けます。ただし、そのレコードの存在は業務完了を意味しません。保存済みで未完了の作業をワーカーが再試行できる必要があります。再受信だけを理由に、返信や CRM 更新をもう一度起動しないでください。

デプロイ時にはポーリング担当を明示的に引き継ぎます。停止し忘れた開発プロセスや、大きい offset を使う手動診断も、保存経路の外で更新を確認済みにしてしまいます。「参照だけの調査」で本番の進捗を変えないことが重要です。

制約と UnifyPort との境界

Telegram は受信更新を 24 時間を超えて保持しないと説明しています。チェックポイントは無期限のアーカイブではありません。offset を小さくしても、確認済みまたは期限切れの更新は復元できません。停止期間にはデータ欠落の可能性を調べてください。

既存アカウントの受信箱や LINE と Telegram をまとめた窓口が目的なら、まず Telegram Bot API Webhook と統一受信 Webhook の比較でアカウントの違いを確認します。UnifyPort の非公式インターフェースは message.received などの正規化イベントを使い、Bot のポーリング offset は使いません。

UnifyPort の配信リファレンスには別の確認契約があります。signing_secret を設定し、タイムスタンプ、ドット、未加工のリクエストバイト列に対する HMAC-SHA256 で X-Device-Signature を検証します。その後、イベントを永続化してから成功レスポンスを返します。通常イベントの再試行では X-Device-Event-Id が再利用されます。REST によるメッセージ履歴取得 API や、受信できなかったペイロードの再送保証はありません。確認済み Bot API 更新の復旧サービスではない点にも注意してください。

よくある質問

getUpdates が同じ更新を返し続けるのはなぜですか?

次のリクエストに対象の update_id より大きい offset が渡されているか、再起動後に保存済みチェックポイントを読み込んでいるかを確認します。キューを捨てず、安全に重複排除してください。

AI や CRM の完了まで offset を進めてはいけませんか?

完全な更新を永続化し、業務タスクを独立して再試行できるなら、待つ必要はありません。メモリだけでの保持は永続的な引き渡しではありません。

返信が必ず一度だけになる保証はありますか?

ありません。原子的な受信保存は取りこぼしの一部を防ぎますが、送信成功後に完了記録が失敗することはあります。その境界にも冪等性や照合が必要です。

次のステップと出典

ポーリングループのコミット境界を点検してください。接続済みアカウントの受信側を実装する場合は、Bot API の offset ロジックではなく Webhook 配信契約を使います。

UnifyPort API

メッセージ連携を安定したプロダクトパイプラインへ。

まずは 1 つの API で送信を始め、標準イベントですべての inbound メッセージを業務システムへ戻しましょう。