โดยทั่วไป global variables แย่ แตบางครั้ง global ในที่ถูกต้อง ทำให้โค้ดง่ายมาก
ใน Rails คุณอาจ set data ครั้งเดียวระหว่าง request แตใช้ในทุก layer ของแอป User ไหน request? Permission อะไร? Database connection ไหน?
ข้อมูลนี้ควร available ทั่วโค้ด ส่งทุกที่แค่ noise แตใช้ Ruby global variable หรือ class variable ก็ทำปัญหา Threads หลายตัว overwrite ได้ และกลายเป็น mess ใหญ่ และอาจมี bad data ที่ unrecoverable
คุณต้องการสิ่งที่ global แต แค่ request ของคุณ
Plain Ruby วิธี
ปกติเห็นจัดการด้วย Thread.current[] 长这样:
Thread.current[:current_user] = user ง่ายพอ 解决方案不錯 แตมี 2 drawbacks ใหญ่:
-
ใครสักคนอาจ overwrite data ของคุณโดยไม่ได้ตั้งใจ
ถ้า 2 คนเลือก key เดียวกัน? มันจะกดข้อมูลกัน gems ของตัวเอง อาจไม่ใช่ปัญหา แตถ้าเขียน gems หรือแอปใหญ่และ messy ต้องคิดถึง
-
มันไม่ well-structured
Thread.current[]แค่ถุงข้อมูลใหญ่ ไม่ง่ายเอกสาร ถ้าอยากรู้ดึงอะไรได้ ต้องหาว่าใส่อะไรเข้าไปแตถ้าใส่ global data เยอะจนนี้เป็นปัญหา คุณมีอะไรกังวลมากกว่า
Thread.current[]แล้ว แต่ยังคงเป็นจุดที่ควรจำ
มี solution 好กว่า Thread.current[] ไหม? Rails เองจัดการ request-local data เยอะ และ ActiveSupport มี pattern อื่นให้ follow
Rails วิธี
ขุด ActiveSupport อาจเจอ ActiveSupport::PerThreadRegistry จากชื่อบอกได้ว่านี่คือสิ่งที่เราหา
ด้วย ActiveSupport::PerThreadRegistry ตัวอย่างก่อนจะกลายเป็น:
class RequestRegistry extend ActiveSupport::PerThreadRegistry attr_accessor :current_user, :current_permissions end RequestRegistry.current_user = user RequestRegistry.current_user # => user งานเพิ่มนิดหน่อย แตกับ ActiveSupport::PerThreadRegistry:
-
มีที่วางเอกสาร ใครดูคลาส จะรู้ global data ที่คาดหวัง และใช้ได้อะไร
-
ได้ API ที่ดูดี ดูเหมือนแค่ set class variables นั่นคือสิ่งที่คุณทำถ้าไม่ต้องกังวล threads
-
Data ที่ set บน class จะ namespaced ไม่ต้องกังวล
RequestRegistry.current_userชนPhoneNumberApiRegistry.current_userแยกกันสนิท
Rails ใช้ ActiveSupport::PerThreadRegistry เอง สำหรับ ActiveRecord connection handling, storing EXPLAIN queries, ActiveSupport::Notifications หาตัวอย่างดีๆ มันเองเป็นที่เริ่มต้นดี
ตอน Threads และ Requests ไม่ Match
บาง Rails servers single thread handle multiple requests หมายถึงของใน PerThreadRegistry จะค้างอยู่ request ถัดไป น่าจะไม่ใช่สิ่งที่อยาก
ทำอย่าง Rails: cleanup สิ่งใน PerThreadRegistry หลังเสร็จ ขึ้นอยู่กับอะไรที่ใส่ อาจทำอยู่แล้ว
ในคอมเมนต์ MattB แนะนำ request_store มันทำงานเหมือน Thread.current[] แตเคลียร์ตัวเองหลังแต่ละ request
เปิดประตูให้สร้าง PerRequestRegistry รวมความปลอดภัยของ request_store กับ API ของ PerThreadRegistry ถ้ามีใครทำแล้ว ผมอยากรู้!
ลองดู
Global data อาจแย่ Globals เยอะนำไปสู่ unmaintainable code
แตถ้ามีเหตุผลเก็บ data ที่ central สำหรับ request ทั้งหมด ไปไกลกว่า Thread.current[] ลอง ActiveSupport::PerThreadRegistry หรือ request_store ถ้าอื่นไม่อาจ จะทำให้จัดการ global data น้อยลงความเสี่ยง