← บทความทั้งหมด
คู่มือ

Telegram getUpdates offset: ป้องกันการประมวลผลซ้ำและอัปเดตสูญหาย

Telegram จะถือว่าอัปเดตได้รับการยืนยันเมื่อคุณเรียก getUpdates ด้วย offset ที่มากกว่า update_id ของอัปเดตนั้น การได้รับ response เพียงอย่างเดียวยังไม่ใช่การยืนยัน เพื่อป้องกันงานสูญหาย ให้บันทึกอัปเดตลงที่เก็บข้อมูลถาวรก่อนส่งคำขอถัดไปด้วย offset ที่สูงขึ้น และบันทึก offset ถัดไปพร้อมข้อมูลนั้น เพื่อรองรับการรีสตาร์ต โดยแยกการตรวจข้อมูลซ้ำออกจากการทำงานทางธุรกิจ

ประเด็นสำคัญ

  • offset คือขอบเขตการยืนยัน ไม่ใช่เลขหน้าหรือจำนวนข้อความ
  • เลื่อนไปข้างหน้าเมื่ออัปเดตที่จะถูกยืนยันถูกจัดเก็บอย่างปลอดภัยครบแล้วเท่านั้น
  • ให้แต่ละบอตมีผู้รับผิดชอบ polling ที่ทำงานอยู่เพียงหนึ่งตัว แล้วขยาย worker ปลายทางแทน
  • กล่องรับข้อมูลถาวรช่วยปกป้องขั้นตอนรับเข้า แต่การทำงานกับระบบภายนอกยังต้องมีแผน retry ของตัวเอง

บทความนี้สมมติว่าคุณเลือกใช้ polling แล้ว หากยังตั้งค่า webhook อยู่ ให้ดู ขั้นตอนสลับ getUpdates กับ setWebhook ก่อน ประเด็นที่นี่คือการ commit ข้อมูลระหว่างคำขอ polling ที่สำเร็จ ไม่ใช่การเลือกรูปแบบรับข้อมูลใหม่

getUpdates offset ยืนยันอะไรบ้าง

เอกสาร Telegram Bot API อย่างเป็นทางการ ระบุว่า offset คือรหัสของอัปเดตแรกที่ต้องการให้ส่งกลับ หากไม่ระบุ จะเริ่มจากอัปเดตที่เก่าที่สุดซึ่งยังไม่ถูกยืนยัน เมื่อเรียกครั้งถัดไปด้วย offset ที่สูงกว่ารหัสของอัปเดตนั้น อัปเดตก็จะถูกยืนยัน

ตัวอย่างสมมติ: response มีอัปเดต 8100, 8101 และ 8102 หากเรียกครั้งถัดไปด้วย offset=8103 จะยืนยันทั้งสามรายการ แม้แอปของคุณเพิ่งประมวลผลรายการสุดท้ายเพียงรายการเดียว Telegram ไม่ได้ตรวจฐานข้อมูลหรือรอให้ CRM ทำงานเสร็จ

วิธีลัดความเสี่ยงการออกแบบที่ปลอดภัยกว่า
บันทึก ID สูงสุดก่อนเก็บทั้งชุดหลังรีสตาร์ตอาจข้ามอัปเดตที่ยังไม่เคยบันทึกcommit กล่องรับข้อมูลและ offset ถัดไปพร้อมกัน
เลื่อน offset เมื่องานขนานที่เร็วที่สุดเสร็จอัปเดตก่อนหน้าที่ยังไม่เสร็จอาจถูกยืนยันด้วยใช้ความคืบหน้าการจัดเก็บ ไม่ใช่ลำดับที่ worker เสร็จ
เก็บ offset ในหน่วยความจำเท่านั้นcheckpoint หายเมื่อรีสตาร์ตโหลด checkpoint ถาวรแยกตามบอต
ใช้ offset ติดลบเพื่อแก้ข้อมูลซ้ำอัปเดตก่อนหน้าในคิวถูกทิ้งตรวจเจ้าของ polling และการตรวจซ้ำ

Telegram ระบุชัดว่า offset ติดลบจะอ่านจากท้ายคิวและลืมอัปเดตก่อนหน้านั้น จึงไม่ใช่วิธีกู้คืนงาน production ที่ยังต้องใช้

แยกความคืบหน้าการรับเข้าจากสถานะงานธุรกิจ

แนวทางหนึ่งคือเก็บข้อมูลที่แอปเป็นเจ้าของสองส่วน: กล่องรับเข้าที่มีอัปเดตฉบับเต็ม และ checkpoint ที่เก็บ offset ถัดไป ทั้งสองส่วนเป็นแนวคิดของฐานข้อมูลภายใน ไม่ใช่ฟิลด์ใหม่ของ Telegram API

ใช้ตัวตนที่คงที่ของบอตร่วมกับ update_id เป็น unique key ของกล่องรับเข้า อย่าใช้ bot token เองเป็นคีย์ฐานข้อมูลหรือเขียนลง log เก็บอัปเดตทุกประเภทที่ได้รับ รวมถึงประเภทที่ worker ปัจจุบันยังไม่รู้จัก แล้วค่อยจำแนกภายหลัง

ลำดับ transaction ที่แนะนำ:

อ่าน offset ถัดไปที่บันทึกไว้สำหรับบอตนี้
เรียก getUpdates ด้วย offset ดังกล่าว
ถ้าชุดข้อมูลว่าง ให้คง checkpoint เดิม
หากไม่ว่าง ให้เริ่ม transaction ของฐานข้อมูล:
  เพิ่มอัปเดตทุกรายการ โดยไม่เพิ่มซ้ำเมื่อ unique key มีอยู่แล้ว
  บันทึก max(update_id ในชุดนี้) + 1 เป็น offset ถัดไป
commit
เรียก polling ครั้งต่อไปหลัง commit สำเร็จเท่านั้น
ให้ worker แยกต่างหากประมวลผลอัปเดตที่บันทึกไว้

นี่คือ pseudocode สำหรับการออกแบบ ไม่ใช่ polling client ที่สมบูรณ์ โดยสมมติว่ามี poller ที่ทำงานอยู่หนึ่งตัว มีที่เก็บข้อมูลถาวรที่รองรับ transaction และมี unique constraint หาก transaction ล้มเหลว ให้หยุดเลื่อน checkpoint และ retry จากค่าที่บันทึกไว้ อย่าจับข้อผิดพลาดการจัดเก็บแล้วทำต่อด้วย offset ที่สูงกว่า

หากกล่องรับเข้าและ checkpoint อยู่คนละระบบ transaction นี้จะไม่เป็น atomic โดยอัตโนมัติ ต้องออกแบบการส่งต่องานแบบถาวรและการตรวจสอบความสอดคล้อง อย่าถือว่าการเขียนสำเร็จสองครั้งเท่ากับการ commit ครั้งเดียว

ทดสอบตามจุดที่ระบบอาจหยุด

ตารางนี้เป็นกรณีทดสอบที่แนะนำ ไม่ใช่ผลการทดสอบจริง:

จุดที่หยุดพฤติกรรมกู้คืนที่คาดหวัง
หลังรับชุดข้อมูล แต่ก่อน commitโหลด checkpoint เดิม และยอมรับการได้รับข้อมูลซ้ำ
ระหว่าง transactionrollback แล้วต้องไม่มี checkpoint ที่อัปเดตเพียงบางส่วนค้างอยู่
หลัง commit แต่ก่อน polling ครั้งถัดไปโหลด checkpoint ใหม่ และงานที่บันทึกไว้ยังประมวลผลต่อได้
หลังทำงานภายนอกสำเร็จ แต่ก่อนบันทึกว่าเสร็จตรวจสอบสถานะหรือใช้ idempotency ปลายทาง คีย์รับเข้าเพียงอย่างเดียวป้องกันการทำงานซ้ำไม่ได้

อัปเดตซ้ำควรอ้างถึงรายการเดิมในกล่องรับเข้า แต่การมีรายการนั้นไม่ได้หมายความว่างานธุรกิจเสร็จแล้ว worker ต้องค้นหาและ retry งานที่บันทึกไว้แต่ยังไม่เสร็จได้ ในทางกลับกัน การรับข้อมูลซ้ำไม่ควรเริ่มส่งคำตอบหรือแก้ไข CRM อีกรอบโดยอัตโนมัติ

ระหว่าง deploy ให้ส่งต่อสิทธิ์ polling อย่างชัดเจน process สำหรับพัฒนาที่ลืมปิด หรือคำขอตรวจสอบด้วย offset ที่สูงขึ้น อาจยืนยันอัปเดตนอกเส้นทางจัดเก็บของคุณ อย่าให้การตรวจสอบที่คิดว่าเป็น “อ่านอย่างเดียว” เปลี่ยนความคืบหน้าของ production

ข้อจำกัดและขอบเขตของ UnifyPort

Telegram ระบุว่าเก็บอัปเดตขาเข้าไว้ไม่เกิน 24 ชั่วโมง checkpoint จึงไม่ใช่คลังข้อมูลถาวรไม่จำกัดเวลา และการลด offset ไม่สามารถกู้อัปเดตที่ยืนยันแล้วหรือหมดอายุแล้วได้ ควรตรวจช่วงที่ระบบหยุดว่าอาจมีข้อมูลสูญหายหรือไม่

หากต้องรับจากบัญชีเดิม หรือรวมงาน LINE กับ Telegram ให้เริ่มจาก เปรียบเทียบ Telegram Bot API webhook กับ webhook ขาเข้าแบบรวม อินเทอร์เฟซที่ไม่เป็นทางการของ UnifyPort ใช้อีเวนต์มาตรฐาน เช่น message.received ไม่ใช่ polling offset ของบอต

เอกสารการส่ง webhook ของ UnifyPort กำหนดข้อตกลงการยืนยันแยกต่างหาก ตั้งค่า signing_secret และตรวจ X-Device-Signature ด้วย HMAC-SHA256 บน timestamp ตามด้วยจุดและไบต์ดิบของ request จากนั้นบันทึกอีเวนต์ถาวรก่อนตอบกลับว่าสำเร็จ การ retry อีเวนต์ทั่วไปใช้ X-Device-Event-Id เดิม UnifyPort ไม่มี REST API สำหรับอ่านประวัติข้อความ และไม่รับประกันการส่ง payload ที่พลาดไปซ้ำ จึงไม่ใช่บริการกู้อัปเดต Bot API ที่ยืนยันแล้ว

คำถามที่พบบ่อย

ทำไม getUpdates ส่งอัปเดตเดิมกลับมาตลอด?

ตรวจว่าคำขอถัดไปใช้ offset ที่มากกว่า update_id เหล่านั้นจริงหรือไม่ และหลังรีสตาร์ตได้โหลด checkpoint ที่บันทึกไว้หรือไม่ ควรตรวจซ้ำอย่างปลอดภัยแทนการทิ้งคิว

ต้องรอ AI หรือ CRM เสร็จก่อนเลื่อน offset หรือไม่?

ไม่จำเป็น หากบันทึกอัปเดตฉบับเต็มแบบถาวรแล้วและ retry งานแยกได้ การเก็บในหน่วยความจำอย่างเดียวไม่ใช่การส่งต่อที่ทนต่อการหยุดทำงาน

วิธีนี้รับประกันว่าส่งคำตอบเพียงครั้งเดียวหรือไม่?

ไม่รับประกัน การจัดเก็บแบบ atomic ป้องกันงานสูญหายบางกรณี แต่การส่งอาจสำเร็จก่อน worker บันทึกว่าเสร็จ ต้องออกแบบ idempotency หรือการตรวจสอบสถานะในจุดนั้นด้วย

ขั้นตอนถัดไปและแหล่งข้อมูล

ตรวจ polling loop ตรงขอบเขตการ commit ฐานข้อมูล หากใช้ตัวรับของบัญชีรับส่งข้อความ ให้ทำตาม ข้อตกลงการส่ง webhook โดยไม่ยกตรรกะ offset ของ Bot API มาใช้

UnifyPort API

เปลี่ยนการเชื่อมต่อข้อความให้เป็น pipeline ผลิตภัณฑ์ที่เสถียร

เริ่มจากการส่งผ่าน API เดียว แล้วส่งข้อความขาเข้าทั้งหมดกลับสู่ระบบธุรกิจของคุณด้วย event มาตรฐาน