ซองจดหมายใส่วางในกรอบขอบเขตบนโต๊ะมิ้นต์มีลูกศรส่งต่อไปงานลูกโดยไม่คัดลอกกองใหญ่

Scoped Values เบื้องต้น: ส่งบริบทแบบ immutable ในขอบเขตเดียว

น้องกายEngineering

เวลาอยากส่ง requestId หรือสิทธิ์ผู้ใช้ลงงานลูกโดยไม่โยงพารามิเตอร์ทุกเมธอด คนมักใช้ ThreadLocal ครับ แต่กับ virtual threads จำนวนมาก การคัดลอก/ล้างค่าผิดจุดทำให้รั่วหรือกินหน่วยความจำง่าย

Scoped Values (ดู JEP 506) เป็นค่า immutable ที่ผูกกับ ขอบเขตแบบ structured แล้วส่งต่อให้งานลูกในกรอบนั้น โดยไม่ต้องให้ทุกเธรดมีสำเนาที่เขียนทับได้

แนวคิดสั้น ๆ

  1. ประกาศคีย์เป็น static final ScopedValue<...>
  2. ผูกค่าด้วย ScopedValue.where(KEY, value).run(() -> { ... }) (หรือ API เทียบเท่าตามรุ่น)
  3. อ่านด้วย KEY.get() ภายในขอบเขตเท่านั้น
  4. นอกขอบเขตต้องไม่พึ่งค่าเดิม

ต่างจาก ThreadLocal ที่มัก mutable และอายุยืนตามเธรด Scoped Value ตั้งใจให้สั้นและอ่านอย่างเดียวในกรอบ

ตัวอย่าง static-only (แนวคิด)

บล็อกด้านล่างเป็น fragment สำหรับอ่านแนวคิด ไม่ได้ตั้งเป็น verify suite เพราะ API สุดท้ายขึ้นกับรุ่น JDK ที่ทีมใช้ (preview → final ตาม JEP)

// static-only fragment — conceptual ScopedValue usage
import java.lang.ScopedValue;

public final class RequestScope {
    public static final ScopedValue<String> REQUEST_ID = ScopedValue.newInstance();

    private RequestScope() {}

    public static void handle(String requestId, Runnable work) {
        ScopedValue.where(REQUEST_ID, requestId).run(work);
    }

    public static String currentRequestId() {
        return REQUEST_ID.get();
    }
}

เมื่อไหร่ควรย้ายจาก ThreadLocal

  • ส่ง context ของ request / trace id ลง Structured Concurrency
  • ต้องการค่าที่ห้ามเขียนทับกลางทาง
  • ใช้ virtual threads จำนวนมากแล้วกลัว ThreadLocal บloat

ยังไม่ต้องรีบไล่แทนที่ทุก ThreadLocal ที่เป็น cache ของไลบรารีเก่า ให้เริ่มจาก context ของแอปคุณเอง

Recap

  • ScopedValue = บริบท immutable ในขอบเขตชัด
  • ประกาศ static final แล้วผูกด้วย where(...).run(...)
  • เหมาะกับ request context มากกว่าแคช mutable
  • เช็กรุ่น JDK / JEP 506 ก่อนเปิดในโปรดักชันครับ