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

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

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

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

IndexedDB,‏ OPFS ו-Cache Storage API נתמכים בכל הדפדפנים המודרניים. הן אסינכרניות ולא יחסמו את ה-thread הראשי (אבל יש גם וריאנט סינכרוני של OPFS שזמין רק ב-web workers). אפשר לגשת אליהם מהאובייקט window, מ-web workers ומ-service workers, כך שאפשר להשתמש בהם בכל מקום בקוד.

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

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

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

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

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

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

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

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

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

  • Chrome מאפשר לדפדפן להשתמש ב-80% משטח הדיסק הכולל. מקור יכול לנצל עד 60% משטח האחסון הכולל. אפשר להשתמש ב-StorageManager API כדי לקבוע את המכסה המקסימלית שזמינה. יכול להיות שבדפדפנים אחרים המבוססים על Chromium הדברים יהיו שונים.
    • במצב פרטי, Chrome מפחית את נפח האחסון שהמקור יכול להשתמש בו ל-5% בערך מנפח האחסון הכולל בכונן.
    • אם המשתמש הפעיל ב-Chrome את האפשרות 'קובצי ה-cookie ונתוני האתר ינוקו בסגירה של כל החלונות', מכסת האחסון תופחת באופן משמעותי ל-300MB לכל היותר.
  • בדפדפן 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 ו-Cache 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.`);
}

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

בדיקה

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

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

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

איך מטפלים חריגה מהמכסה?

מה עושים כשחורגים מהמכסה? הדבר החשוב ביותר הוא תמיד לזהות ולטפל בשגיאות כתיבה, בין שמדובר ב-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
  }
};

Cache API

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

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 יתחילו להוציא נתונים כשהדפדפן ייגמר לו מקום. הם ינקו קודם את כל נתוני האתרים מהמקור שבו השתמשתם הכי פחות לאחרונה, ואז את הנתונים מהמקור הבא, עד שהדפדפן לא יחרוג מהמגבלה.
  • Firefox יתחיל להוציא נתונים כשנפח האחסון הזמין יתמלא, והמערכת תמחק את כל נתוני האתר מהמקור שנעשה בו הכי פחות בשימוש קודם, ואז מהמקור הבא, עד שהדפדפן יחרוג מהמגבלה.
  • בעבר, Safari לא הפעילה פינוי נתונים, אבל לאחרונה הוספנו מגבלת 7 ימים חדשה לכל האחסון שניתן לכתיבה (ראו בהמשך).

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

קטגוריות אחסון

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

בונוס: למה כדאי להשתמש ב-wrapper ל-IndexedDB

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

בונוס: SQLite Wasm

אחרי שהוצאנו משימוש את Web SQL והסרנו אותו מ-Chrome, Google עבדה עם המנהלים של מסד הנתונים הפופולרי SQLite כדי להציע תחליף ל-Web SQL שמבוסס על SQLite. במאמר SQLite Wasm בדפדפן שמגובל על ידי מערכת הקבצים הפרטית של המקור מוסבר איך משתמשים ב-SQLite Wasm.

סיכום

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

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

תודה

תודה מיוחדת ל-Jarryd Goodman,‏ Phil Walton,‏ Eiji Kitamura,‏ Daniel Murphy,‏ Darwin Huang,‏ Josh Bell,‏ Marijn Kruisselbrink ו-Victor Costan על בדיקת המדריך הזה. תודה לאייג'י קיטמורה, אדי אוסמאני ומארק כהן שכתבו את המאמרים המקוריים שעליהם הם מבוססים. איג'י כתב כלי שימושי בשם Browser Storage Abuser, ששימש לאימות ההתנהגות הנוכחית. כך תוכלו לאחסן כמה שיותר נתונים ולראות את מגבלות האחסון בדפדפן. תודה ל-François Beaufort שבדק את Safari כדי להבין את מגבלות האחסון שלו, ול-Thomas Steiner שנוסף מידע על מערכת הקבצים הפרטית של המקור, על קטגוריות האחסון, על SQLite Wasm ועל עדכון תוכן כללי שיתבצע בשנת 2024.

התמונה הראשית (Hero) היא של Guillaume Bolduc ב-Unsplash.