ข้ามไปยังเนื้อหา
Pre-MVP โค้ดเบส 1.0 ยังไม่ใช่ผลิตภัณฑ์ที่เผยแพร่ — ดู ขอบเขตผลิตภัณฑ์ 1.0

Scheduling แบบ end to end

ทั้งสองเซอร์วิสบนเครื่องเดียว ไม่ต้องมีบัญชี Cloudflare ไม่ต้องมี credential จริง: ติดตั้งปลั๊กอินเข้าไปใน Dee Wan บนเครื่อง local ตั้งเวลาการเผยแพร่จริง ทำให้ runner execute มัน แล้วทำให้มันล้มเหลวในแต่ละ รูปแบบที่มันควรจะล้มเหลว

README ของปลั๊กอิน คือเอกสารอ้างอิงสำหรับ route, การตั้งค่า, ความหมายของตารางเวลา และตาราง retry หน้านี้คือลำดับขั้นตอน เอกสารฝั่ง core อยู่ใน docs/plugins/ ของ repository ของ Dee Wan — protocol v1, ความปลอดภัย, การทดสอบ

ไม่มีขั้นตอนใดด้านล่างที่ต้องใช้โดเมนจริง token จริง หรือ Worker ที่ deploy แล้ว อย่าชี้ส่วนใดของมันไปที่ ไซต์ production

  • Node ตามเวอร์ชันที่ pin ไว้ใน .nvmrc ของ repository ของ Dee Wan และติดตั้ง dependency ของมันแล้ว
  • พอร์ตว่างสองพอร์ต: 8787 สำหรับ admin Worker ของ Dee Wan, 8788 สำหรับปลั๊กอินนี้ และ 5173 สำหรับ admin UI
  • openssl สำหรับสร้าง key สุ่มหนึ่งตัว

จาก root ของ repository ของ Dee Wan:

Terminal window
npm run local:migrate
npm run local:seed # prints the sign-in it creates
npm run local:api # admin Worker on http://localhost:8787 (leave running)
npm run local:admin # admin UI on http://localhost:5173 (leave running)

ก่อนเริ่ม local:api ให้ใส่การตั้งค่าสามอย่างใน backend/.dev.vars:

ENVIRONMENT=development
PLUGIN_DEV_ORIGINS=http://localhost:8788
BACKEND_URL=http://localhost:8787

เหตุผลของแต่ละตัว:

  • ENVIRONMENT=development คือสิ่งที่อนุญาตให้ URL ของปลั๊กอินเป็น http:// ได้ตั้งแต่แรก
  • PLUGIN_DEV_ORIGINS คือ allowlist แบบ origin ตรงตัว ตรวจกับ URL ของ manifest, base, activation และ management ไม่มี wildcard ไม่มีทางลัดสำหรับช่วง private หากไม่มีรายการนี้ การติดตั้งจาก http://localhost:8788 จะถูกปฏิเสธเป็น url_not_https
  • BACKEND_URL ถูกส่งให้ปลั๊กอินนี้เป็น dee_wan_base_url หากไม่มี การติดตั้งจะปฏิเสธด้วย backend_url_unset แทนที่จะ activate สิ่งที่ไม่สามารถเรียกกลับได้
Terminal window
cd plugins/scheduling
cp wrangler.example.jsonc wrangler.jsonc # fill in the D1 id it prints below
npx wrangler d1 create dee-wan-scheduling
npx wrangler d1 migrations apply dee-wan-scheduling --local

.dev.vars ในไดเรกทอรีนี้:

ENVIRONMENT=development
PUBLIC_ORIGIN=http://localhost:8788
DEE_WAN_DEV_ORIGINS=http://localhost:8787
ADMIN_SECRET=local-admin-secret-0123456789
TOKEN_KEY=<openssl rand -base64 32>

DEE_WAN_DEV_ORIGINS คือภาพสะท้อนของ PLUGIN_DEV_ORIGINS ในฝั่งปลั๊กอินนี้: base URL ของ Dee Wan ที่ไม่ใช่ HTTPS เพียงหนึ่งเดียวที่มันจะยอมรับ และเฉพาะใน development เท่านั้น TOKEN_KEY เข้ารหัส installation token ที่เก็บไว้ขณะอยู่นิ่ง (at rest) — หากทำหาย ทุก installation ต้องถูก rotate

Terminal window
npx wrangler dev --port 8788 --test-scheduled # leave running
curl http://localhost:8788/dee-wan/manifest.json # sanity: the manifest names your origin
Terminal window
curl -X POST -H "Authorization: Bearer local-admin-secret-0123456789" \
http://localhost:8788/admin/activation-codes

ใช้ครั้งเดียว อายุ 15 นาที เก็บแบบ hash ขณะอยู่นิ่ง body ว่างจะออกโค้ด activate ส่วน {"installation_id": "<id>"} จะออกโค้ด rotate สำหรับ installation นั้นเท่านั้น

เข้าสู่ระบบที่ http://localhost:5173 ด้วยบัญชีที่ local:seed พิมพ์ออกมา จากนั้น Settings → Plugins → Install:

  • manifest URL http://localhost:8788/dee-wan/manifest.json
  • activation code จากขั้นตอนที่ 3
  • capability: ทั้งสี่ตัว สำหรับการทดลองนี้
  • โมเดล: ติ๊ก Article (หรืออะไรก็ตามที่ไซต์ที่ seed ไว้มี)

ใช้ UI แทน curl: route ด้านการดูแลระบบต้องการ session จริง header ของไซต์ และ สิทธิ์ site:plugins และ UI มีครบทั้งสามอย่าง

Settings → Plugins หลัง Review: capability ทั้งสี่ของ manifest และโมเดลของไซต์ ติ๊กไว้ทั้งหมด และ activation code ที่ว่างอยู่

Settings → Plugins หลัง Install: Scheduling 0.1.0 อยู่ในสถานะ Active พร้อม grant และโมเดลของมัน

สิ่งที่เกิดขึ้นตามลำดับ: Dee Wan ดึงและ validate manifest บันทึก installation ที่อยู่ในสถานะรอ ออก token แล้ว POST มันพร้อมโค้ดของคุณไปยัง http://localhost:8788/dee-wan/activate และทำเครื่องหมาย installation ว่า active เฉพาะหลังจากปลั๊กอินนี้ตอบ 2xx เท่านั้น ตรวจสอบทั้งสองฝั่ง:

Terminal window
npx wrangler d1 execute dee-wan-scheduling --local \
--command "SELECT id, site_id, dee_wan_base_url FROM installation"

คอลัมน์ token_ciphertext คือหน้าตาของ token ที่ถูกเก็บไว้ ไม่มีคอลัมน์ บรรทัด log หรือ route ใดในฝั่งใดที่จะแสดง token ดิบให้คุณเห็น

ใน Dee Wan: เปิดรายการ draft ในโมเดลนั้น แล้วใช้ action Schedule ที่ manifest ประกาศไว้ นั่นคือ delegated launch: Dee Wan ออกโค้ดแบบใช้ครั้งเดียว browser ของคุณไปถึง http://localhost:8788/installations/<id>?dee_wan_launch=… แบ็กเอนด์ของปลั๊กอินนี้แลกเปลี่ยนมันเป็น delegation ตั้ง session cookie ของตัวเอง และ redirect ไปยังหน้าเดิมโดยไม่มีโค้ดใน URL

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

Terminal window
npx wrangler d1 execute dee-wan-scheduling --local \
--command "SELECT id, kind, target_state, pinned_version_id, expected_workflow_revision, local_datetime, timezone, due_at, status FROM schedule"

pinned_version_id คือเวอร์ชันที่ตรงตัวซึ่งจะถูกเผยแพร่ — ไม่ใช่ “อะไรก็ตามที่เป็นปัจจุบันตอนที่มันรัน”

cron trigger ไม่ทำงานเองใน wrangler dev เมื่อใช้ --test-scheduled:

Terminal window
curl "http://localhost:8788/__scheduled?cron=*+*+*+*+*"

หนึ่ง tick จะ claim แถวที่ถึงกำหนดได้สูงสุด 20 แถวพร้อม lease ส่งคำสั่งหนึ่งคำสั่งต่อแถว และสรุปผลแถวเหล่านั้น จากนั้น:

Terminal window
npx wrangler d1 execute dee-wan-scheduling --local \
--command "SELECT id, status, attempt_count, last_code, last_message FROM schedule"
npx wrangler d1 execute dee-wan-scheduling --local \
--command "SELECT schedule_id, attempt, code, http_status FROM schedule_attempt ORDER BY attempt"

ตารางเวลาที่เสร็จสมบูรณ์จะแสดง status = completed, last_code = transition_applied ใน Dee Wan รายการนั้นถูกเผยแพร่แล้ว translation group ทั้งกลุ่มถูกย้ายไปพร้อมกัน และประวัติเนื้อหาแสดง Scheduling plugin เป็นผู้กระทำ — ไม่ใช่บัญชีผู้ใช้ของคุณ และไม่ใช่บัญชีที่ถูกปลอมขึ้น

tick อีกครั้งก่อนถึงเวลากำหนด และจะไม่มีอะไรถูก claim tick สองครั้งหลังเวลากำหนด และ tick ที่สองจะไม่พบ อะไรให้ทำ นั่นคือ lease บวกกับ idempotency key และคุ้มค่าที่จะเห็นสักครั้ง

แต่ละข้อต่อไปนี้คือสถานะจริงที่ผู้ใช้จะเจอ ทำในไซต์สำหรับทดลอง

เพื่อดู ทำสิ่งนี้ สิ่งที่คาดหวัง
scheduled_target_stale ตั้งเวลาการเผยแพร่ จากนั้นแก้ไขและบันทึก draft แล้ว tick refused ไม่มีการ retry และรายการยังคงไม่ถูกเผยแพร่
workflow_conflict ตั้งเวลาการเผยแพร่ จากนั้นเผยแพร่รายการด้วยมือ แล้ว tick refused ไม่มีการ retry
dependent_content เผยแพร่ A เชื่อม B ที่เผยแพร่แล้วเข้ากับมัน ตั้งเวลายกเลิกการเผยแพร่ A แล้ว tick refused และ Dee Wan ระบุชื่อ dependency
requester_unauthorized ตั้งเวลาในฐานะผู้ใช้คนหนึ่ง ลบบทบาท (role) ของผู้ใช้นั้นใน Dee Wan แล้ว tick refused สิทธิ์อำนาจถูกอ่านใหม่ตอน execute ไม่มีวันถูกส่งต่อไปใช้
plugin_unauthorized ตั้งเวลา จากนั้นปิดใช้งานหรือ uninstall ใน Settings → Plugins แล้ว tick refused ตารางเวลาแจ้งว่าปลั๊กอินเสียสิทธิ์เข้าถึง
temporary_failure หยุด Dee Wan Worker แล้ว tick retrying พร้อม next_attempt_at เริ่มใหม่แล้ว tick อีกครั้งเพื่อให้เสร็จสมบูรณ์
retry_exhausted หยุด Dee Wan และ tick จนเกิน MAX_ATTEMPTS หรือเกิน due_at + MAX_LATENESS_MS refused retry_exhausted และไม่มีอะไรถูกส่งอีกเลย
การยกเลิก ยกเลิกตารางเวลาที่รออยู่ใน UI ของปลั๊กอิน cancelled Dee Wan ไม่ถูกเรียกเลย และไม่มีอะไรถูกย้อนกลับ
ตารางเวลาที่กำลังรัน ยกเลิกขณะที่ tick กำลังทำงานอยู่ execution_in_progress และ UI โหลดสถานะสุดท้ายใหม่

ตั้ง MAX_ATTEMPTS=2 และ MAX_LATENESS_MS=60000 ใน .dev.vars เพื่อให้ถึงเพดานภายในหนึ่งนาที แทนที่จะเป็น สิบห้านาที

หลัง retry_exhausted ที่ตามหลังความล้มเหลวระดับ transport ผลลัพธ์จะ ไม่ทราบ อย่างแท้จริง — คำสั่งอาจถูกนำไปใช้แล้วโดย Dee Wan ที่ไม่สามารถตอบกลับได้ ให้ตรวจสอบรายการใน Dee Wan นั่นคือเหตุผลที่ตารางเวลาแจ้งเช่นนั้น แทนที่จะอ้างว่าล้มเหลว

rotation ซึ่งผู้ดูแลระบบ (operator) ควรฝึกไว้ก่อนที่จะต้องใช้:

Terminal window
curl -X POST -H "Authorization: Bearer local-admin-secret-0123456789" \
-H 'content-type: application/json' \
--data '{"installation_id":"<installation id>"}' \
http://localhost:8788/admin/activation-codes

จากนั้น Settings → Plugins → Rotate ด้วยโค้ดนั้น Dee Wan ออก token ใหม่ ส่งมอบมาที่นี่ และหลังจากนั้น เท่านั้นจึงสลับ hash ที่เก็บไว้ token เก่ายังใช้ได้ในช่วงซ้อนทับที่มีขอบเขตจำกัด และ request แรกที่ ถือ token ใหม่จะยุติช่วงซ้อนทับนั้น หากปลั๊กอินนี้ปฏิเสธ token เก่ายังคงใช้งานได้ — ตรวจสอบ สิ่งนั้นด้วย โดย rotate ด้วยโค้ดที่คุณใช้ไปแล้ว

การ uninstall ใน Dee Wan จะ revoke token ทันทีและถาวร ข้อมูลของปลั๊กอินนี้ยังคงอยู่: ตารางเวลาใด ๆ ที่เหลืออยู่จะปฏิเสธด้วย plugin_unauthorized เมื่อถึงกำหนด ซึ่งเป็นสิ่งที่ผู้ใช้ควร เห็น แทนที่จะหายไปอย่างเงียบ ๆ การระบุผู้กระทำใน audit ของ Dee Wan ก็ยังคงอยู่ เพราะ workflow event เก็บ snapshot ของ installation ไว้

หากต้องการลบข้อมูลของปลั๊กอินนี้สำหรับ installation หนึ่ง ผู้ดูแลระบบต้องร้องขอ:

Terminal window
curl -X POST -H "Authorization: Bearer local-admin-secret-0123456789" \
http://localhost:8788/admin/installations/<installation id>/delete-data

ถูกปฏิเสธด้วย 409 execution_in_progress ขณะที่ตารางเวลาของ installation นั้นกำลังรันอยู่ ไม่มี การล้างข้อมูลตาม retention อัตโนมัติ การลบเป็นการกระทำที่ต้องมีคนทำ

Terminal window
rm -rf .wrangler/state # in plugins/scheduling: the local plugin D1

ฝั่ง Dee Wan reset ด้วยฐานข้อมูล local ของตัวเอง รัน npm run local:migrate และ npm run local:seed ใหม่

สิ่งที่สิ่งนี้พิสูจน์ และสิ่งที่ไม่ได้พิสูจน์

หัวข้อที่มีชื่อว่า “สิ่งที่สิ่งนี้พิสูจน์ และสิ่งที่ไม่ได้พิสูจน์”

มันพิสูจน์ว่าทั้งสองเซอร์วิสสอดคล้องกัน: manifest, activation, launch delegation, transition แบบมีเงื่อนไข ที่ถูก pin ไว้, การปฏิเสธที่มีชื่อ, retry, revocation และการลบ

มันไม่ได้พิสูจน์อะไรเกี่ยวกับการ deploy — DNS จริง latency ของ D1 จริง cron schedule จริง หรือ Worker สองตัวภายใต้โหลด และไม่ได้ใช้แทน suite ใด suite หนึ่งได้: npm test ที่นี่ และ npx vitest run test/plugin-*.test.ts ในแบ็กเอนด์ของ Dee Wan คือสิ่งที่รันในทุกการเปลี่ยนแปลง และทั้งคู่ จงใจใช้ตัวแทน (double) สำหรับอีกฝั่ง เพื่อให้ความล้มเหลวระบุชื่อเซอร์วิสเดียวแทนที่จะเป็นสองเซอร์วิส