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

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

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

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

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

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

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

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

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

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

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

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

File System API ו-FileWriter API מספקים שיטות לקריאה ולכתיבה של קבצים במערכת קבצים ב-sandbox. השיטה הזו היא אסינכרונית, אבל לא מומלץ להשתמש בה כי היא זמינה רק בדפדפנים המבוססים על 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 המודרניים שמבוססים על הבטחה, הוא מבוסס על אירועים. חבילות Promise כמו 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 על בדיקת המדריך הזה. תודה לאייג'י קיטמורה (Eiji Kitamura), לאדי אוסמאני (Addy Osmani) ולמארק כהן (Marc Cohen) שכתבו את המאמרים המקוריים שעליהם המאמר הזה מבוסס. איג'י כתב כלי שימושי שנקרא Browser Storage Abuser, ששימש לאימות ההתנהגות הנוכחית. כך תוכלו לאחסן כמה שיותר נתונים ולראות את מגבלות האחסון בדפדפן. תודה ל-François Beaufort שבדק את Safari כדי להבין את מגבלות האחסון שלו, ול-Thomas Steiner שנוסף מידע על מערכת הקבצים הפרטית של המקור, על קטגוריות האחסון, על SQLite Wasm ועל עדכון תוכן כללי שיתבצע בשנת 2024.

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