มาสคอตขนมปังกลมต้นฉบับกำลังตัดเชือก context ที่ผูกถุงข้อมูลกับนาฬิกาทรายบนโต๊ะสว่าง

Bun 1.4.2 แก้ AsyncLocalStorage กัก context ไว้นานเกินงาน

พี่โชว์Engineering

ถ้าเซิร์ฟเวอร์เพิ่งขึ้น Bun 1.4.1 แล้วหน่วยความจำโตช้า ๆ ทั้งที่ request จบครบ ให้ตรวจ AsyncLocalStorage ก่อนครับ Bun 1.4.2 แก้ regression ที่ timer, immediate หรือ pending promise ซึ่งสร้างใน exit() หรือ run() ซ้อน ยังยึด outer store ไว้ตลอดอายุของงานนั้น

WHY: ค่าอ่านถูก แต่หน่วยความจำยังไม่คืน

บั๊กนี้หลอกตาเพราะ getStore() คืนค่าถูกต้อง ไม่ได้ทำให้ request คนหนึ่งอ่าน context ของอีกคน แต่ object ใหญ่ที่เก็บ request metadata, trace หรือ cache ชั่วคราวยังถูกอ้างถึง เหมือนพนักงานคืนกุญแจห้องแล้ว แต่เชือกคล้องกระเป๋ายังผูกกับลูกบิดอยู่ garbage collector จึงเก็บกระเป๋าไม่ได้

แพตช์เดียวกันแก้ bun build เปลี่ยนชื่อตัวแปรชนกับ let จน Elysia build พัง, ลำดับ event online ของ worker, crash บางกรณี และ CMYK JPEG ด้วย แต่บทนี้ล็อกมุมที่ memory retention เพื่อไม่ยืด release note หนึ่งชุดเป็นข่าวซ้ำหลายบท

ตัวอย่างนี้เป็น smoke test เรื่องขอบเขต context ไม่ใช่ benchmark การคืน heap เพราะการพิสูจน์ retention ต้องใช้ heap snapshot และ workload ที่ควบคุมได้

import { AsyncLocalStorage } from "node:async_hooks";

const storage = new AsyncLocalStorage();

await storage.run({ requestId: "outer" }, async () => {
  if (storage.getStore()?.requestId !== "outer") throw new Error("missing outer store");
  storage.exit(() => {
    if (storage.getStore() !== undefined) throw new Error("exit must hide the store");
  });
  if (storage.getStore()?.requestId !== "outer") throw new Error("outer store was not restored");
});

if (storage.getStore() !== undefined) throw new Error("store escaped the run scope");
console.log("context boundaries passed");

วิธี rollout

ตรึง 1.4.2 ใน canary แล้ว replay traffic รูปแบบเดียวกับ production วัด RSS, heap used และจำนวน request context ที่ยังอยู่หลัง idle period เปรียบเทียบกับ 1.4.1 อย่าใช้ bun upgrade แบบไม่ตรึงเวอร์ชันใน fleet พร้อมกัน เพราะถ้ามี regression ใหม่จะย้อนยาก

ถ้าไม่ได้อยู่บน 1.4.1 บั๊กนี้ไม่ใช่เหตุผลฉุกเฉินเพียงข้อเดียว แต่ 1.4.2 ยังแก้ crash และ build regression ควรผ่าน test suite และ build artifact เดิมก่อนเลื่อนเป็นรุ่นมาตรฐาน

Recap

  • 1.4.2 แก้ outer store ถูก timer/promise ยึดไว้นานเกินไป
  • smoke test ตรวจ semantics; heap snapshot ตรวจ retention
  • canary ด้วย workload จริงก่อนอัปทั้ง fleet
  • ตรวจ build ของ Elysia และ worker event ไปพร้อมกัน