Eviction และ Persistence
สองโหมดของ storage
หัวข้อที่มีชื่อว่า “สองโหมดของ storage”storage ของทุก origin อยู่ในหนึ่งในสองโหมด:
- Best-effort — โหมดเริ่มต้น browser อาจ ลบข้อมูลจาก origin โดยไม่มีการแจ้งเตือนหากอุปกรณ์มีพื้นที่ดิสก์ไม่เพียงพอ
- Persistent — browser จะ ไม่ ลบข้อมูลของ origin โดยอัตโนมัติ ข้อมูลจะถูกลบเมื่อผู้ใช้ลบเองเท่านั้น
แอปพลิเคชันเว็บส่วนใหญ่ทำงานในโหมด best-effort ซึ่งเพียงพอสำหรับ cache ที่ไม่สำคัญ หากแอปของคุณต้องพึ่งพาข้อมูล cache เพื่อการทำงานออฟไลน์ คุณควรร้องขอ persistent storage
เมื่อใดที่เกิด eviction?
หัวข้อที่มีชื่อว่า “เมื่อใดที่เกิด eviction?”Eviction เป็นการตัดสินใจของ browser ที่เกิดจาก storage pressure ขั้นตอนทั่วไปคือ:
- อุปกรณ์มีพื้นที่ว่างน้อยลง
- browser ตัดสินใจว่าต้องคืน storage
- เดินผ่าน origin ตามลำดับ least-recently-used (LRU) โดยประมาณ — origin ที่ไม่ได้เข้าชมมานานเป็นผู้สมัครก่อน
- origin แบบ best-effort ถูกล้าง; origin แบบ persistent ถูกปล่อยไว้
Eviction ส่งผลต่อ storage ทั้งหมด ของ origin พร้อมกัน: Cache API, IndexedDB และกลไกอื่น ๆ ที่นับ quota ถูกล้างพร้อมกัน
วิธีที่ persist() ปกป้องข้อมูล
หัวข้อที่มีชื่อว่า “วิธีที่ persist() ปกป้องข้อมูล”การเรียก navigator.storage.persist() ส่งคำขอไปยัง browser เพื่อย้าย origin ไปยังโหมด persistent browser อาจอนุมัติหรือไม่อนุมัติ:
const granted = await navigator.storage.persist();if (granted) { console.log('Storage is persistent — data is safe');} else { console.log('Best-effort only — browser may evict under pressure');}ปัจจัยที่มีอิทธิพลต่อการตัดสินใจของ browser ได้แก่ การที่ผู้ใช้ติดตั้ง PWA ไว้ที่ home screen คะแนน engagement ของ site และการที่ผู้ใช้ได้ให้สิทธิ์ permission อื่น ๆ ไว้แล้ว
การตรวจสอบสถานะปัจจุบัน
หัวข้อที่มีชื่อว่า “การตรวจสอบสถานะปัจจุบัน”ใช้ navigator.storage.persisted() เมื่อเริ่มต้นแอป:
const isAlreadyPersistent = await navigator.storage.persisted();
if (!isAlreadyPersistent) { const granted = await navigator.storage.persist(); if (!granted) { console.warn('Could not get persistent storage. Cached data may be lost.'); }}คำแนะนำในทางปฏิบัติ
หัวข้อที่มีชื่อว่า “คำแนะนำในทางปฏิบัติ”- อย่าสมมติ ว่า
persist()จะคืนค่าtrueเสมอ — จัดการกรณีfalseให้เหมาะสม - เรียก
persisted()ก่อนเพื่อหลีกเลี่ยงการแจ้งเตือนซ้ำซ้อน - สำหรับแอปที่ต้องทำงานออฟไลน์ ควรเก็บข้อมูล diagnostic ไว้ใน localStorage เพื่อตรวจจับการสูญเสียข้อมูลเมื่อโหลดหน้าครั้งถัดไป
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
| อยู่ในโหมด best-effort (ค่าเริ่มต้น) | ไม่ต้องขอ permission จากผู้ใช้ ใช้งานได้ทันที เหมาะกับ cache ที่ไม่สำคัญ | browser อาจ evict ข้อมูลได้ตลอดเวลาโดยไม่แจ้งเตือนเมื่อดิสก์ตึง |
เรียก navigator.storage.persist() เพื่อขอ persistent storage | ข้อมูลปลอดภัยจากการ evict อัตโนมัติ เหมาะกับแอปที่พึ่งพาข้อมูลออฟไลน์ | ต้องผ่านเงื่อนไขของ browser (เช่น engagement, การติดตั้ง PWA) และอาจถูกปฏิเสธ ทำให้โค้ดต้อง handle ทั้งสองกรณี |
เช็ค persisted() ก่อนขอ persist() ทุกครั้ง | หลีกเลี่ยงการขอ permission ซ้ำซ้อนที่รบกวนผู้ใช้ | เพิ่ม async call หนึ่งจังหวะก่อนเริ่ม flow หลัก |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- สมมติว่า
persist()คืนค่าtrueเสมอโดยไม่เช็คผลลัพธ์ — browser อาจปฏิเสธคำขอเงียบ ๆ ต้อง handle กรณีfalseและแจ้งเตือนผู้ใช้หรือ fallback ตามความเหมาะสม - ไม่เคยเรียก
navigator.storage.persisted()ก่อนpersist()— ทำให้เรียกขอ permission ซ้ำทุกครั้งที่โหลดแอป ทั้งที่อาจได้รับสิทธิ์ไปแล้ว - เข้าใจว่า eviction ลบเฉพาะ cache ที่เก่าที่สุด — จริง ๆ แล้ว browser ล้าง storage ทั้งหมดของ origin พร้อมกัน ทั้ง Cache API และ IndexedDB ไม่ได้เลือกลบทีละรายการ
💡 ตัวอย่างจากของจริง
Twitter Lite — ขอ persistent storage เพื่อป้องกันไม่ให้ app shell ที่ cache ไว้ถูก evict ระหว่างที่ผู้ใช้ engagement ต่ำ
Wikipedia — บทความที่ผู้ใช้บันทึกไว้อ่านออฟไลน์ต้องพึ่ง persistent storage ไม่งั้นอาจถูกลบทิ้งเมื่อดิสก์ของอุปกรณ์ใกล้เต็ม