เวิร์กเกอร์ล่ม แต่การซิงค์ไม่ล่ม

ฆ่าเวิร์กเกอร์กลางแบตช์แล้วติดตามผู้ติดต่อหนึ่งราย: เคอร์เซอร์ใดถูกบันทึก ทำไมการเขียนที่เล่นซ้ำจึงไม่ลงสองครั้ง และใครเป็นเจ้าของการซิงค์ระหว่างรีสตาร์ต จากนั้นคือเหตุการณ์สองครั้งที่แสดงว่าการกู้คืนเป็นเพียงครึ่งหนึ่งของงาน

กู้คืนการซิงค์ข้อมูลที่ล้มเหลว

จะเกิดอะไรขึ้นกับการซิงค์ที่กำลังทำงาน เมื่อเวิร์กเกอร์ล่มกลางแบตช์?

เคอร์เซอร์เลื่อนเฉพาะเมื่อจบแบตช์ที่เรียบร้อย ดังนั้นเวิร์กเกอร์ที่ถูกฆ่ากลางแบตช์จะทิ้งเคอร์เซอร์ไว้ที่ต้นแบตช์นั้น และการรันครั้งถัดไปจะอ่านหน้าเดิม คีย์การส่งของแต่ละเรคคอร์ดสร้างจากเคอร์เซอร์ของหน้าและตำแหน่งของเรคคอร์ด และสมุดบันทึกการเขียนที่เสร็จแล้วซึ่งเก็บไว้เจ็ดวันป้องกันไม่ให้การเล่นซ้ำลงสองครั้ง สิทธิ์การถือครอง (lease) หนึ่งรายการต่อหนึ่งการซิงค์ป้องกันไม่ให้สองการรันทำงานพร้อมกัน นั่นคือครึ่งที่ง่าย การซิงค์หนึ่งรายการล้มเหลวซ้ำแล้วซ้ำเล่า รันแล้วรันเล่า ก่อนที่จะมีสิ่งใดหยุดมันชั่วคราว และเวิร์กสเปซหนึ่งเงียบอยู่โดยมีข้อผิดพลาดแต่ไม่มีความสำเร็จเลย ก่อนที่จะมีสิ่งใดสังเกตเห็น ตอนนี้จึงมีเบรกเกอร์ที่หยุดการซิงค์ชั่วคราวหลังล้มเหลวติดต่อกันห้าครั้ง และการแจ้งเตือนรายชั่วโมงเมื่อผ่านไปยี่สิบสี่ชั่วโมงโดยไม่มีความสำเร็จ

เรียนรู้เพิ่มเติม
ในหน้านี้

ในการบรรยายที่ AI Engineer เผยแพร่เมื่อ 3 ตุลาคม 2026 ผู้สนับสนุนนักพัฒนา (developer advocate) ของ Crusoe เริ่มงานเทรน PyTorch บนโหนด A100 สองโหนด จากนั้นผู้บรรยายแจ้งระบบมอนิเตอร์ของเดโมเองว่า GPU ตัวหนึ่งเกิดข้อผิดพลาด XID 79 โหนดนั้นถูกทำเครื่องหมายว่าใช้งานไม่ได้และงานถูกยกเลิก โปรเซสได้รับ SIGTERM และมี “เวลาสูงสุดสองนาทีในการจัดการ SIGTERM นั้น เพื่อบันทึก checkpoint หรือเขียนล็อกที่ค้างอยู่ออกให้หมด” โหนดสำรองเข้ามาแทน และ “งาน slurm จะรีสตาร์ต โหลด checkpoint แล้วเทรนต่อจากจุดที่ค้างไว้” ตั้งแต่ต้นจนจบใช้เวลาไม่ถึงสิบห้านาทีเล็กน้อย ไม่มีใครต้องถูกเรียกตัวมาแก้เหตุ

การซิงค์เป็นสัตว์ที่ตัวเล็กกว่างานเทรนมาก แต่คำถามยังเหมือนเดิม การรันต้องมีอะไรบ้าง เพื่อให้การฆ่าเวิร์กเกอร์เสียเวลาแค่ไม่กี่นาทีและไม่เกิดข้อมูลซ้ำ

นี่คือคำตอบสำหรับตัวรันการซิงค์ของ Tajo เอง อ่านจากโค้ด ไม่ใช่จากโบรชัวร์ Reverse ETL อธิบายรูปร่างของงานนี้ ส่วนบทความนี้คือสิ่งที่เกิดขึ้นเมื่องานนั้นตาย

ติดตามผู้ติดต่อหนึ่งราย

การรันอ่านแหล่งข้อมูลทีละหน้า โดยเริ่มจากเคอร์เซอร์ที่บันทึกไว้ ให้เคอร์เซอร์ของหน้าชื่อ c3 และผู้ติดต่อของเราคือเรคคอร์ดที่ 212 บนหน้านั้น มีสามข้อที่เป็นจริงเกี่ยวกับผู้ติดต่อรายนี้ก่อนที่จะมีการเขียนใด ๆ

ข้อแรก ผู้ติดต่อรายนี้มีคีย์การส่ง คีย์นี้เป็นค่าแฮชของ id ของการซิงค์ แหล่งข้อมูล เคอร์เซอร์ของหน้า ตำแหน่งบนหน้า และตัวเรคคอร์ดเอง เคอร์เซอร์และเรคคอร์ดถูกนำเข้าไปในแฮชและไม่เคยถูกฝังไว้ในคีย์ คีย์จึงไม่มีที่อยู่อีเมลติดอยู่

ข้อสอง การเขียนผ่านสมุดบันทึก (ledger) ไม่ได้ไปยังปลายทางโดยตรง ก่อนเรียกปลายทาง คีย์จะถูกจองไว้ในสถานะรอดำเนินการ เมื่อเขียนสำเร็จ แถวนั้นจะถูกทำเครื่องหมายว่าเสร็จแล้วและเก็บไว้เจ็ดวัน

ข้อสาม เคอร์เซอร์ยังไม่ขยับ เคอร์เซอร์เลื่อนเฉพาะตอนจบแบตช์ เฉพาะเมื่อทุกเรคคอร์ดในแบตช์นั้นลงปลายทางแล้ว และเฉพาะหลังจากการรันพิสูจน์แล้วว่ายังเป็นเจ้าของการซิงค์อยู่

ทีนี้ ฆ่าเวิร์กเกอร์ระหว่างการเขียนกับ checkpoint

เมื่อได้รับ SIGTERM เวิร์กเกอร์คิวทุกตัวจะเคลียร์งานที่ค้างภายใต้เส้นตายร่วมสิบห้าวินาที และโปรเซสจะออกหลังยี่สิบห้าวินาทีไม่ว่าอะไรยังทำงานอยู่ ส่วน systemd ให้เวลาสี่สิบห้าวินาทีก่อนจะฆ่า การรันที่ไม่จบภายในช่วงนั้นยังถูกทำเครื่องหมายว่ากำลังดำเนินการ และเวิร์กเกอร์ถัดไปจะรับต่อผ่านกลไกกู้คืนงานที่ค้าง (stalled-job recovery) ของคิว ในกรณีฆ่าแบบบังคับ (hard kill) จะไม่มีการเคลียร์งานเลย ไม่ว่าทางใด ข้อเท็จจริงสองข้อเดิมยังคงอยู่ ปลายทางมีผู้ติดต่อของเราแล้ว เคอร์เซอร์ของเรายังเป็น c3

การเล่นซ้ำ

การรันครั้งถัดไปเริ่มที่ c3 อ่านหน้าเดิม สร้างคีย์เดิมสำหรับเรคคอร์ดที่ 212 แล้วถามสมุดบันทึก สมุดบันทึกตอบว่าเสร็จแล้ว ปลายทางจะไม่ถูกเรียก ผลลัพธ์ที่เก็บไว้ถูกเล่นซ้ำและการรันทำงานต่อไป ในเส้นทางแบบ bulk ซึ่ง endpoint นำเข้าของ Brevo รับทั้งหน้าในการเรียกครั้งเดียว การตรวจแบบเดียวกันจะทำทีละคีย์ก่อนสร้างคำขอ หน้าที่ทุกคีย์เสร็จแล้วจะไม่ส่งอะไรเลย

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

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

ใครเป็นเจ้าของการซิงค์ระหว่างรีสตาร์ต

การซิงค์หนึ่งรายการมีการรันเป็นเจ้าของได้ครั้งละหนึ่งการรันเท่านั้น การรันจะรับสิทธิ์การถือครองใน Redis พร้อมโทเค็นของตัวเอง อายุสิบห้านาที และต่ออายุทุกห้านาที ก่อน checkpoint ทุกครั้ง การรันจะยืนยันว่ายังถือสิทธิ์นั้นอยู่ ถ้าไม่ได้ถือแล้วก็จะหยุด การรันจะถูกบันทึกด้วยประโยคเรียบง่ายที่ผู้ใช้จะได้อ่าน: “The run lost its slot part-way through and stopped to avoid writing twice.” (การรันเสียสิทธิ์กลางทางและหยุดเพื่อไม่ให้เขียนซ้ำสองครั้ง) การรันที่สองที่ชนกับเจ้าของที่ยังทำงานอยู่จะไม่ถูกลองใหม่ มันจบด้วย “Another run of this sync was already in progress” (มีการรันอื่นของการซิงค์นี้กำลังทำงานอยู่แล้ว) และการรันตามกำหนดครั้งถัดไปจะเริ่มตามปกติ

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

จนถึงตรงนี้คือครึ่งที่ง่าย การล่มหนึ่งครั้งเสียแค่การเล่นซ้ำหนึ่งครั้ง และไม่มีอะไรลงสองครั้ง เหตุการณ์สองครั้งสอนอีกครึ่งหนึ่ง

ล้มเหลวแล้วล้มเหลวอีก

การซิงค์หนึ่งรายการล้มเหลวซ้ำแล้วซ้ำเล่า ไม่ใช่ในลูปถี่ ๆ แต่ครั้งละหนึ่งรอบของตัวจัดตาราง รันแล้วรันเล่า ด้วยข้อความเดิมทุกครั้งว่า custom id มีเครื่องหมายโคลอนไม่ได้ การรันไม่เคยไปถึงปลายทาง มันตายระหว่างถูกเข้าคิว ก่อนที่ตัวรันใดจะเห็น ดังนั้นการแจ้งเตือนที่ผูกกับตัวรันจึงไม่มีอะไรให้ผูกเลย

ไม่มีอะไรเฝ้าดูความล้มเหลวที่ต่อเนื่องนั้น นั่นคือข้อค้นพบทั้งหมด

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

สิ่งที่เบรกเกอร์ไม่ได้ทำคือการแก้ไขอะไร Crusoe เปลี่ยนโหนดและรีสตาร์ตงานโดยไม่ต้องถาม Tajo หยุดและถาม: การซิงค์ที่หยุดชั่วคราวจะหยุดอยู่อย่างนั้นจนกว่าคนจะแก้ต้นเหตุและเผยแพร่การซิงค์อีกครั้ง ค่าเริ่มต้นที่ระมัดระวังนี้เป็นความตั้งใจ: การลองใหม่อัตโนมัติของการซิงค์ที่เขียนลงข้อมูลลูกค้าก็คือการเขียนในตัวมันเอง

ความเงียบที่ไม่มีใครเห็น

ข้อโต้แย้งนี้สมเหตุสมผล และเป็นอย่างนี้: “เบรกเกอร์ที่หยุดชั่วคราวหลังล้มเหลวห้าครั้งก็น่าจะจับเหตุการณ์ที่สองได้เหมือนกัน ทำไมต้องสร้างตัวเฝ้าดูตัวที่สอง?” เพราะเหตุการณ์ที่สองไม่ได้ดูเหมือนความล้มเหลว เป็นเวลานานที่เวิร์กสเปซหนึ่งมีการซิงค์ที่เปิดใช้งาน มีแถวข้อผิดพลาดกองเพิ่มขึ้นเรื่อย ๆ และไม่มีความสำเร็จเลยแม้แต่ครั้งเดียว และไม่มีอะไรแจ้งเตือน ทุกสัญญาณที่มีอยู่ผูกกับกิจกรรม: การรันล้มเหลว webhook เข้ามา งบประมาณถูกใช้เกิน ความสำเร็จที่เป็นศูนย์ไม่ใช่เหตุการณ์ มันคือการไม่มีเหตุการณ์ และเบรกเกอร์ทำงานเฉพาะเมื่อมีการบันทึกความล้มเหลว ตัวจัดตารางที่ตายแล้ว คิวที่ติดขัด เวิร์กเกอร์ที่ไม่เคยบูตขึ้น สิ่งเหล่านี้ไม่บันทึกอะไรเลย และเบรกเกอร์ก็ไม่เคยตื่น

ด้ามจับ dead-man ของหัวรถจักรทำงานกลับกัน คนขับต้องกดมันไว้ตลอด และความเงียบจะหยุดรถไฟ เวอร์ชันสำหรับการซิงค์ทำงานชั่วโมงละครั้ง ที่นาทีที่สิบสามของทุกชั่วโมง สำหรับทุกเวิร์กสเปซที่มีการซิงค์ที่เปิดใช้งาน มันถามคำถามเดียว: มีการรันใดสำเร็จในยี่สิบสี่ชั่วโมงที่ผ่านมาหรือไม่? เวิร์กสเปซที่การซิงค์ที่เปิดใช้งานเก่าที่สุดถูกเปิดเมื่อไม่ถึงหนึ่งวันก่อนจะถูกปล่อยไว้ เพราะยังไม่มีเวลาหนึ่งวันที่จะสำเร็จ เวิร์กสเปซที่เก่ากว่านั้นและเงียบจะได้รับการแจ้งเตือนระดับวิกฤตหนึ่งครั้ง การแจ้งเตือนบอกว่าเป็นความเงียบแบบใด: มีการรันถูกบันทึกแต่ไม่มีครั้งใดสำเร็จ หรือไม่มีการรันถูกบันทึกเลย มันทำงานครั้งเดียวต่อเหตุการณ์ ตัวทำเครื่องหมายใน Redis จะปิดมันไว้จนถึงการรันที่สำเร็จครั้งแรก จากนั้นจะถูกล้าง เพื่อให้เหตุการณ์ครั้งต่อไปแจ้งเตือนได้อีก

ตัวเฝ้าดูทั้งสองถูกเพิ่มเข้ามาในคอมมิตเดียว หลังเกิดเหตุแล้ว คอมเมนต์ของเบรกเกอร์บอกว่ามันจะหยุดเหตุการณ์แรกได้ที่การรันครั้งที่ห้า คอมเมนต์ของ dead-man บอกว่าการซิงค์ที่เปิดใช้งานมานานแต่ไม่มีการรันเลยต้องแจ้งเตือน ทั้งสองไม่มีอยู่ตอนที่ต้องการ เราเขียนมันขึ้นทีหลัง พร้อมบันทึกเหตุการณ์ทั้งสองไว้ในส่วนหัวของไฟล์ เพื่อไม่ให้ใครลืมว่าทำไม

สิ่งที่สร้างแล้ว และสิ่งที่ยังไม่ได้สร้าง

สร้างแล้ว ณ เวลาที่เขียน:

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

ยังไม่ได้สร้าง และควรรู้ก่อนพึ่งพา:

  • dead-man เฝ้าดูเวิร์กสเปซ ไม่ใช่การซิงค์ การซิงค์ที่ปกติหนึ่งรายการจะบังการซิงค์อีกรายการที่เงียบอยู่ในเวิร์กสเปซเดียวกัน
  • การแจ้งเตือนของมันไปถึงผู้ดูแลระบบของ Tajo: บรรทัดล็อกเสมอ และ Sentry, webhook หรืออีเมลในที่ที่ตั้งค่าไว้ มันไม่ปรากฏบนหน้าของการซิงค์ แต่การหยุดชั่วคราวของเบรกเกอร์ปรากฏ
  • ไม่มีอะไรรีสตาร์ตการซิงค์ที่หยุดชั่วคราว คนเป็นผู้ทำ
  • การกู้คืนหลังการฆ่าแบบบังคับไม่เร็ว สิทธิ์การถือครองอาจค้างได้สิบห้านาที และการกวาดรอหนึ่งชั่วโมง
  • เกณฑ์ทั้งสอง คือห้าความล้มเหลวและยี่สิบสี่ชั่วโมง เป็นการตั้งค่าระดับการติดตั้งใช้งาน คุณตั้งค่าแยกต่อการซิงค์ไม่ได้

สายยาวของขั้นตอนที่เกือบสมบูรณ์แบบก็ยังล้มเหลว ทางออกคือกำหนดสถานะปลายทางแล้วตรวจหามัน บทความนี้คือครึ่งเชิงปฏิบัติการของเรื่องนั้น กำหนดว่าการรันที่ตายทิ้งอะไรไว้ และตรวจหาความว่างเปล่าที่ความเงียบทิ้งไว้

ประโยคปิดของ Crusoe คือ “ความล้มเหลวเป็นสิ่งที่หลีกเลี่ยงไม่ได้” ของเราก้าวไปอีกขั้นหนึ่ง การล่มคือครึ่งที่ง่าย เพราะมันทิ้งแถวไว้ ความเงียบคือครึ่งที่เราต้องออกไปตามหา

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

จะเกิดอะไรขึ้นกับการซิงค์ที่กำลังทำงาน เมื่อเวิร์กเกอร์ล่มกลางแบตช์?

เคอร์เซอร์ยังชี้ไปที่จุดเริ่มต้นของแบตช์ที่ถูกขัดจังหวะ เพราะ Tajo เลื่อนเคอร์เซอร์ก็ต่อเมื่อแบตช์ลงปลายทางเรียบร้อยแล้วเท่านั้น การรันครั้งถัดไปจึงอ่านหน้าเดิมอีกครั้ง แต่ละเรคคอร์ดมีคีย์การส่ง (delivery key) ที่สร้างจากเคอร์เซอร์ของหน้าและตำแหน่งของเรคคอร์ด และมีสมุดบันทึก (ledger) ที่เก็บการเขียนที่เสร็จแล้วทุกรายการไว้เจ็ดวัน เรคคอร์ดที่เขียนไปแล้วจึงถูกเล่นซ้ำจากสมุดบันทึก แทนที่จะถูกส่งไปยังปลายทางเป็นครั้งที่สอง

ทำไมการซิงค์ที่เล่นซ้ำจึงไม่เขียนผู้ติดต่อรายเดิมสองครั้ง?

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

การแจ้งเตือนแบบ dead-man สำหรับการซิงค์คืออะไร?

การตรวจที่ทำงานเมื่อบางสิ่งหายไป ไม่ใช่เมื่อเกิดความล้มเหลว ทุกชั่วโมง Tajo จะดูทุกเวิร์กสเปซที่มีการซิงค์ที่เปิดใช้งาน และแจ้งเตือนเมื่อไม่มีการรันใดสำเร็จในยี่สิบสี่ชั่วโมงที่ผ่านมา รวมถึงกรณีที่ไม่มีการบันทึกการรันเลย การแจ้งเตือนทำงานครั้งเดียวต่อเหตุการณ์ และพร้อมทำงานอีกครั้งหลังการรันที่สำเร็จครั้งแรก มันมีอยู่เพราะการแจ้งเตือนความล้มเหลวมองเห็นได้เฉพาะความล้มเหลวที่ถูกบันทึกไว้

ขอสิทธิ์ใช้งานล่วงหน้า

กรอกชื่อและที่อยู่อีเมลของคุณ เราจะส่งลิงก์เพื่อยืนยันคำขอเข้าใช้งาน Tajo ทางอีเมล

รับ Brevo

หน้านี้ในรูปแบบ Markdown: worker-died-sync-didnt.md

กำลังโหลด Markdown…
เปิดใน Claude เปิดใน ChatGPT