נפח אחסון לאינטרנט

יש אפשרויות רבות ושונות לאחסון נתונים בדפדפן. איזה מהם הכי מתאים לצרכים שלכם?

חיבורי אינטרנט עשויים להיות לא יציבים או לא קיימים תוך כדי תנועה, ולכן תמיכה במצב אופליין וביצועים אמינים הם תכונות נפוצות באפליקציות מסוג Progressive Web App. גם בסביבה אלחוטית מושלמת, שימוש מושכל בשמירה במטמון ובשיטות אחסון אחרות יכול לשפר משמעותית את חוויית המשתמש. יש מספר דרכים לשמור במטמון את משאבי האפליקציות הסטטיים (HTML, JavaScript, CSS, תמונות וכו') ואת הנתונים (נתוני משתמשים, כתבות חדשותיות וכו'). אבל מהו הפתרון הטוב ביותר? כמה אפשר לאחסן? איך אתם מונעים את ההוצאה שלו?

במה כדאי להשתמש?

הנה המלצה כללית לאחסון משאבים:

IndexedDB ו-cache Storage API נתמכים בכל דפדפן מודרני. שתיהן אסינכרוניות, והן לא יחסמו את ה-thread הראשי. אפשר לגשת אליהם מהאובייקט window, מ-Web work ומ-Service Workers, כך שקל להשתמש בהם בכל מקום בקוד.

מה לגבי מנגנוני אחסון אחרים?

יש מספר מנגנוני אחסון נוספים בדפדפן, אבל השימוש בהם מוגבל ועלול לגרום לבעיות משמעותיות בביצועים.

SessionStorage הוא ספציפי לכרטיסייה, והיקף משך החיים של הכרטיסייה. הוא יכול להיות שימושי לאחסון כמויות קטנות של מידע ספציפי לסשנים, למשל מפתח IndexedDB. חשוב להשתמש בו בזהירות כי הוא סינכרוני והוא יחסום את ה-thread הראשי. היא מוגבלת לכ-5MB ויכולה להכיל רק מחרוזות. מכיוון שהיא ספציפית לכרטיסייה, לא ניתן לגשת אליה מעובדי אינטרנט או מ-Service Workers.

כדאי להימנע מ-LocalStorage כי הוא סינכרוני והוא יחסום את ה-thread הראשי. הקובץ מוגבל לכ-5MB ויכול להכיל רק מחרוזות. אין גישה ל-LocalStorage מעובדי אינטרנט או מ-service worker.

לקובצי cookie יש שימושים משלהם, אבל אין להשתמש בהם לאחסון. קובצי cookie נשלחים עם כל בקשת HTTP, לכן אחסון של יותר מכמות קטנה של נתונים יגדיל באופן משמעותי את הגודל של כל בקשת אינטרנט. הם סינכרוניים ולא נגישים מעובדי אינטרנט. בדומה ל-LocalStorage ו-SessionStorage, קובצי cookie מוגבלים למחרוזות בלבד.

ב-File System API וב-FileWriter API יש שיטות לקריאה ולכתיבה של קבצים במערכת קבצים שמוגנת בארגז חול. למרות שזה אסינכרוני, לא מומלץ כי הוא זמין רק בדפדפנים המבוססים על Chromium.

File System Access API נועד להקל על משתמשים לקרוא ולערוך קבצים במערכת הקבצים המקומית שלהם. המשתמש חייב להעניק הרשאה לפני שהדף יוכל לקרוא או לכתוב בכל קובץ מקומי, וההרשאות לא נשמרות בסשנים שונים.

לא להשתמש ב-WebSQL, וצריך להעביר את השימוש הקיים ל-IndexedDB. התמיכה הוסרה מכמעט כל הדפדפנים העיקריים. צוות W3C הפסיק לתחזק את מפרט Web SQL ב-2010, ללא כוונה לבצע עדכונים נוספים.

לא להשתמש במטמון של האפליקציה, וצריך להעביר את השימוש הקיים ל-Service Workers ול-Cache API. הוא הוצא משימוש והתמיכה בו תוסר מהדפדפנים בעתיד.

כמה אפשר לאחסן?

בקיצור, הרבה, לפחות כמה מאות מגה-בייט ולפעמים מאות ג'יגה-בייט או יותר. הטמעות הדפדפן משתנות, אבל נפח האחסון הזמין מבוסס בדרך כלל על נפח האחסון שזמין במכשיר.

  • Chrome מאפשר לדפדפן לנצל עד 80% משטח הדיסק הכולל. מקור יכול להשתמש עד 60% מנפח האחסון הכולל. על מנת לקבוע את המכסה המקסימלית שזמינה, אפשר להשתמש ב-StorageManager API. ייתכן שדפדפנים אחרים מבוססי Chromium יהיו שונים.
    • במצב פרטי, Chrome מצמצם את נפח האחסון שמקור יכול להשתמש בו לכ-5% מנפח האחסון הכולל.
    • אם המשתמש הפעיל את האפשרות 'ניקוי קובצי cookie ונתוני אתרים בעת סגירת כל החלונות' ב-Chrome, מכסת האחסון תוקטן משמעותית ל-300MB לערך.
    • לפרטים על ההטמעה של Chrome, ראו PR #3896.
  • דפדפן Internet Explorer 10 ואילך יכול לאחסן עד 250MB, ולהציג למשתמשים בקשה אם נעשה שימוש ביותר מ-10MB.
  • Firefox מאפשר לדפדפן לנצל עד 50% משטח הדיסק הפנוי. קבוצת eTLD+1 (למשל, example.com, www.example.com ו-foo.bar.example.com) עשויים להשתמש ב-2GB לכל היותר. אפשר להשתמש ב-StorageManager API כדי לקבוע כמה נפח אחסון עדיין פנוי.
  • נראה ש-Safari (במחשבים ובניידים) מאפשר כ-1GB. כשמגיעים למגבלה, דפדפן Safari מציג בקשה למשתמשים ומגדילים את המגבלה ב-200MB. לא הצלחתי למצוא מסמכים רשמיים בנושא.
    • אם נוספה PWA למסך הבית ב-Safari לנייד, נראה שהיא יוצרת מאגר אחסון חדש, ושום דבר לא משותף בין ה-PWA ל-Safari לנייד. אחרי הגעה למכסה של PWA שהותקנו, לא תהיה אפשרות לבקש נפח אחסון נוסף.

בעבר, אם אתר חרג מסף מסוים של אחסון נתונים, הדפדפן ביקש מהמשתמש להעניק הרשאה כדי להשתמש בנתונים נוספים. לדוגמה, אם המקור היה גדול מ-50MB, הדפדפן ינחה את המשתמש לאפשר אחסון בנפח של עד 100MB, ואז לשאול שוב במרווחים של 50MB.

כיום, רוב הדפדפנים המודרניים לא מציגים בקשה למשתמש, והם יאפשרו לאתר לנצל את המכסה שהוקצתה לו. נראה שהיוצא מן הכלל הוא Safari, שבו מוצגת בקשה במקרה של חריגה ממכסת האחסון ובקשת הרשאה להגדלת המכסה שהוקצתה. אם מקור ינסה להשתמש ביותר מהמכסה שהוקצתה לו, ייכשלו ניסיונות נוספים לכתוב נתונים.

איך בודקים כמה נפח אחסון זמין?

בדפדפנים רבים אפשר להשתמש ב-StorageManager API כדי לקבוע את נפח האחסון הזמין למקור ואת נפח האחסון שנמצא בשימוש. הוא מדווח על המספר הכולל של הבייטים ששימשו את IndexedDB ואת ממשק ה-API של המטמון, ומאפשר לחשב את נפח האחסון הפנוי המשוער.

if (navigator.storage && navigator.storage.estimate) {
  const quota = await navigator.storage.estimate();
  // quota.usage -> Number of bytes used.
  // quota.quota -> Maximum number of bytes available.
  const percentageUsed = (quota.usage / quota.quota) * 100;
  console.log(`You've used ${percentageUsed}% of the available storage.`);
  const remaining = quota.quota - quota.usage;
  console.log(`You can write up to ${remaining} more bytes.`);
}

ה-StorageManager עדיין לא מוטמע בכל הדפדפנים, ולכן עליכם לזהות אותו לפני השימוש. גם אם היא זמינה, עדיין תצטרכו לאתר שגיאות שקשורות למכסת החריגה (ראו בהמשך). במקרים מסוימים, יכול להיות שהמכסה הזמינה תחרוג מנפח האחסון הזמין בפועל.

בדיקה

במהלך הפיתוח, ניתן לך להשתמש בכלי הפיתוח של הדפדפן כדי לבדוק את סוגי האחסון השונים ולנקות בקלות את כל הנתונים המאוחסנים.

נוספה תכונה חדשה ב-Chrome 88 שמאפשרת לשנות את שטח האחסון של האתר בחלונית האחסון. התכונה הזו מאפשרת לדמות מכשירים שונים ולבדוק את התנהגות האפליקציות בתרחישים עם זמינות דיסק נמוכה. נכנסים ל-Application (אפליקציה) ואז ל-Storage (אחסון), מסמנים את התיבה Simulate custom storage quota ומזינים מספר חוקי כלשהו כדי לדמות את מכסת האחסון.

חלונית DevTools Storage.

במהלך העבודה על המאמר הזה, כתבתי כלי פשוט במטרה לנסות להשתמש בנפח אחסון גדול ככל האפשר. זו דרך קלה ומהירה להתנסות במנגנוני אחסון שונים ולראות מה קורה כשמנצלים את כל המכסה.

איך מתמודדים עם חריגה מהמכסה?

מה צריך לעשות אם חורגים מהמכסה? הדבר החשוב ביותר הוא שתמיד צריך לאתר שגיאות כתיבה ולטפל בהן, לא משנה אם מדובר ב-QuotaExceededError או במשהו אחר. לאחר מכן, בהתאם לעיצוב האפליקציה, תוכלו להחליט איך לטפל בה. לדוגמה, מחיקת תוכן שלא מתבצעת אליו גישה במשך זמן רב, הסרת נתונים לפי גודל או מתן אפשרות למשתמשים לבחור מה הם רוצים למחוק.

גם ב-IndexedDB וגם ב-Cache API יופק DOMError בשם QuotaExceededError במקרה של חריגה מהמכסה הזמינה.

IndexedDB

אם המקור חרג מהמכסה שלו, הניסיון לכתוב ב-IndexedDB ייכשל. הגורם המטפל ב-onabort() של העסקה יופעל, ויעביר אירוע. האירוע יכלול את DOMException במאפיין השגיאה. בדיקת השגיאה name תחזיר QuotaExceededError.

const transaction = idb.transaction(['entries'], 'readwrite');
transaction.onabort = function(event) {
  const error = event.target.error; // DOMException
  if (error.name == 'QuotaExceededError') {
    // Fallback code goes here
  }
};

ממשק API של מטמון

אם המקור חרג מהמכסה שלו, ניסיונות לכתוב ל-מטמון API ייפסלו עם DOMException של QuotaExceededError.

try {
  const cache = await caches.open('my-cache');
  await cache.add(new Request('/sample1.jpg'));
} catch (err) {
  if (error.name === 'QuotaExceededError') {
    // Fallback code goes here
  }
}

איך פועל פינוי המקום?

אחסון באינטרנט מסווג לשתי קטגוריות: 'המאמץ הטוב ביותר' ו'קבוע'. המשמעות היא שאפשר למחוק את האחסון בדפדפן בלי להפריע למשתמשים, אבל הוא פחות עמיד לנתונים קריטיים או לטווח ארוך. נפח האחסון הקבוע לא נמחק באופן אוטומטי כשנפח האחסון עומד להיגמר. המשתמשים צריכים למחוק את האחסון הזה באופן ידני (דרך הגדרות הדפדפן).

כברירת מחדל, הנתונים של אתר (כולל IndexedDB, cache API וכו') נכללים בקטגוריה של הערך 'הכי טוב'. כלומר, אם האתר לא ביקש אחסון קבוע, הדפדפן עשוי להוציא את נתוני האתר לפי שיקול דעתו, למשל, כשנפח האחסון במכשיר נמוך.

מדיניות הפינוי המתאימה ביותר היא:

  • דפדפנים שמבוססים על Chromium יתחילו להוציא נתונים כשייגמר המקום בדפדפן, וניקוי כל נתוני האתר מהמקור הכי פחות בשימוש לאחרונה, לאחר מכן, עד שהדפדפן לא יחרוג מהמגבלה.
  • דפדפן Internet Explorer 10 ואילך לא יוציא נתונים, אבל המערכת תמנע מהמקור לכתוב נתונים נוספים.
  • דפדפן Firefox יתחיל להוציא נתונים כשנפח האחסון הזמין יתמלא, וכל נתוני האתר ינוקו קודם מהמקור שנעשה בו הכי פחות בשימוש לאחרונה, ואחריהם, עד שהדפדפן לא יחרוג מהמגבלה.
  • בעבר לא הוציאו ב-Safari נתונים, אבל הטמענו לאחרונה מכסה חדשה לשבעה ימים בכל האחסון הניתן לכתיבה (פרטים בהמשך).

החל מגרסה iOS ו-iPadOS 13.4 ו-Safari 13.1 ב-macOS, יש מכסה של שבעה ימים לכל אחסון שניתן לכתיבה של סקריפטים, כולל IndexedDB, רישום Worker ו-cache API. כלומר, אם למשתמש אין אינטראקציה עם האתר, דפדפן Safari יוציא את כל התוכן מהמטמון אחרי שבעה ימים של שימוש ב-Safari. מדיניות ההסרה הזו לא חלה על אפליקציות PWA מותקנות שנוספו למסך הבית. לפרטים המלאים, קראו את המאמר חסימה מלאה של קובצי cookie של צד שלישי ועוד בבלוג WebKit.

בונוס: למה להשתמש ב-wrapper עבור IndexedDB

IndexedDB הוא ממשק API ברמה נמוכה שדורש הגדרה משמעותית לפני השימוש, וזה עלול להכאיב במיוחד לאחסון נתונים פשוטים. בניגוד לרוב ממשקי ה-API המודרניים שמבוססים על הבטחות, הממשק מבוסס על אירועים. פריטי wrapper כמו idb ל-IndexedDB מסתירים חלק מהתכונות העוצמתיות, אבל חשוב יותר, מסתירים את המכונות המורכבות (כמו טרנזקציות, גרסאות סכימה) שכלולות בספריית IndexedDB.

סיכום

חלפו הימים שבהם נפח האחסון המוגבל היה מוגבל, והמשתמש ביקש לאחסן עוד נתונים. אתרים יכולים לאחסן ביעילות את כל המשאבים והנתונים שהם צריכים כדי להפעיל. באמצעות StorageManager API תוכלו לקבוע כמה נפח זמין לכם ובכמה אתם משתמשים. באמצעות אחסון מתמיד תוכלו להגן עליו מפני מחיקה, אלא אם המשתמש מסיר אותו.

מקורות מידע נוספים

תודה

תודה מיוחדת לג'ריד גודמן, פיל וולטון, אייג'י קיטמורה, דניאל מרפי, דרווין הואנג, ג'וש בל, מרין קרואיסלברינק וויקטור קוסטן וויקטור קוסטן. תודה לאיג'י קיטמורה, אדי אוסמאני ומארק כהן שכתבו את המאמרים המקוריים שעליהם מבוססות העובדות האלה. אייג'י כתב כלי מועיל בשם משתמש ניצול לרעה של אחסון בדפדפן, שבעזרתו תוכלו לאמת את ההתנהגות הנוכחית. כך תוכלו לאחסן כמה שיותר נתונים ולראות את מגבלות האחסון בדפדפן. תודה לפרנסואה בופורט שהתעמק ב-Safari כדי להבין מהן מגבלות האחסון.

התמונה הראשית (Hero) מוצגת על ידי Guillaume Bolduc ב-UnFlood.