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
0. สิ่งที่คุณต้องมี
หัวข้อที่มีชื่อว่า “0. สิ่งที่คุณต้องมี”- Node ตามเวอร์ชันที่ pin ไว้ใน
.nvmrcของ repository ของ Dee Wan และติดตั้ง dependency ของมันแล้ว - พอร์ตว่างสองพอร์ต: 8787 สำหรับ admin Worker ของ Dee Wan, 8788 สำหรับปลั๊กอินนี้ และ 5173 สำหรับ admin UI
opensslสำหรับสร้าง key สุ่มหนึ่งตัว
1. เริ่ม Dee Wan
หัวข้อที่มีชื่อว่า “1. เริ่ม Dee Wan”จาก root ของ repository ของ Dee Wan:
npm run local:migratenpm run local:seed # prints the sign-in it createsnpm 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=developmentPLUGIN_DEV_ORIGINS=http://localhost:8788BACKEND_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_httpsBACKEND_URLถูกส่งให้ปลั๊กอินนี้เป็นdee_wan_base_urlหากไม่มี การติดตั้งจะปฏิเสธด้วยbackend_url_unsetแทนที่จะ activate สิ่งที่ไม่สามารถเรียกกลับได้
2. เริ่มปลั๊กอินนี้
หัวข้อที่มีชื่อว่า “2. เริ่มปลั๊กอินนี้”cd plugins/schedulingcp wrangler.example.jsonc wrangler.jsonc # fill in the D1 id it prints belownpx wrangler d1 create dee-wan-schedulingnpx wrangler d1 migrations apply dee-wan-scheduling --local.dev.vars ในไดเรกทอรีนี้:
ENVIRONMENT=developmentPUBLIC_ORIGIN=http://localhost:8788DEE_WAN_DEV_ORIGINS=http://localhost:8787ADMIN_SECRET=local-admin-secret-0123456789TOKEN_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
npx wrangler dev --port 8788 --test-scheduled # leave runningcurl http://localhost:8788/dee-wan/manifest.json # sanity: the manifest names your origin3. ออก activation code
หัวข้อที่มีชื่อว่า “3. ออก activation code”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 นั้นเท่านั้น
4. ติดตั้งใน admin UI
หัวข้อที่มีชื่อว่า “4. ติดตั้งใน admin UI”เข้าสู่ระบบที่ 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 มีครบทั้งสามอย่าง


สิ่งที่เกิดขึ้นตามลำดับ: Dee Wan ดึงและ validate manifest บันทึก installation ที่อยู่ในสถานะรอ
ออก token แล้ว POST มันพร้อมโค้ดของคุณไปยัง http://localhost:8788/dee-wan/activate และทำเครื่องหมาย
installation ว่า active เฉพาะหลังจากปลั๊กอินนี้ตอบ 2xx เท่านั้น ตรวจสอบทั้งสองฝั่ง:
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 ดิบให้คุณเห็น
5. ตั้งเวลาการเผยแพร่
หัวข้อที่มีชื่อว่า “5. ตั้งเวลาการเผยแพร่”ใน 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 ที่คำนวณได้ — คู่นั้นคือหัวใจของเรื่องทั้งหมด และเป็น สิ่งที่ถูกเก็บไว้
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 คือเวอร์ชันที่ตรงตัวซึ่งจะถูกเผยแพร่ — ไม่ใช่ “อะไรก็ตามที่เป็นปัจจุบันตอนที่มันรัน”
6. ทำให้มันรัน
หัวข้อที่มีชื่อว่า “6. ทำให้มันรัน”cron trigger ไม่ทำงานเองใน wrangler dev เมื่อใช้ --test-scheduled:
curl "http://localhost:8788/__scheduled?cron=*+*+*+*+*"หนึ่ง tick จะ claim แถวที่ถึงกำหนดได้สูงสุด 20 แถวพร้อม lease ส่งคำสั่งหนึ่งคำสั่งต่อแถว และสรุปผลแถวเหล่านั้น จากนั้น:
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 และคุ้มค่าที่จะเห็นสักครั้ง
7. ทำให้มันล้มเหลวโดยตั้งใจ
หัวข้อที่มีชื่อว่า “7. ทำให้มันล้มเหลวโดยตั้งใจ”แต่ละข้อต่อไปนี้คือสถานะจริงที่ผู้ใช้จะเจอ ทำในไซต์สำหรับทดลอง
| เพื่อดู | ทำสิ่งนี้ | สิ่งที่คาดหวัง |
|---|---|---|
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
นั่นคือเหตุผลที่ตารางเวลาแจ้งเช่นนั้น แทนที่จะอ้างว่าล้มเหลว
8. Rotate และ uninstall
หัวข้อที่มีชื่อว่า “8. Rotate และ uninstall”rotation ซึ่งผู้ดูแลระบบ (operator) ควรฝึกไว้ก่อนที่จะต้องใช้:
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 หนึ่ง ผู้ดูแลระบบต้องร้องขอ:
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 อัตโนมัติ การลบเป็นการกระทำที่ต้องมีคนทำ
9. Reset
หัวข้อที่มีชื่อว่า “9. Reset”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) สำหรับอีกฝั่ง เพื่อให้ความล้มเหลวระบุชื่อเซอร์วิสเดียวแทนที่จะเป็นสองเซอร์วิส