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

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

חיבורי אינטרנט יכולים להיות לא יציבים או לא קיימים כשנמצאים בדרכים, ולכן תמיכה במצב אופליין וביצועים אמינים הם תכונות נפוצות בProgressive Web App. גם בסביבות אלחוטיות מושלמות, שימוש מושכל בשמירה במטמון ובטכניקות אחסון אחרות יכול לשפר באופן משמעותי את חוויית המשתמש. יש כמה דרכים לשמור במטמון את המשאבים הסטטיים של האפליקציה (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 נועד לאפשר למשתמשים לקרוא ולערוך קבצים במערכת הקבצים המקומית שלהם. המשתמש צריך לתת הרשאה לפני שדף יכול לקרוא או לכתוב בקובץ מקומי, וההרשאות לא נשמרות בין סשנים, אלא אם כן הטיפול בקובץ נשמר במטמון ב-IndexedDB. ‫File System Access API מתאים במיוחד לתרחישים לדוגמה כמו עורכים, שבהם צריך לפתוח קובץ, לשנות אותו ואז אולי לשמור את השינויים בחזרה בקובץ.

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

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

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

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

איך עובד תהליך הפינוי?

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

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

מדיניות הפינוי של Best Effort היא:

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

החל מגרסאות iOS ו-iPadOS 13.4 ו-Safari 13.1 ב-macOS, יש מגבלה של שבעה ימים על כל האחסון שניתן לכתיבה באמצעות סקריפט, כולל IndexedDB, רישום של קובצי שירות (service worker) ו-Cache API. כלומר, Safari יסיר את כל התוכן מהמטמון אחרי שבעה ימים של שימוש ב-Safari, אם המשתמש לא יבצע פעולה באתר. מדיניות הפינוי הזו לא חלה על אפליקציות PWA מותקנות שנוספו למסך הבית. לפרטים מלאים, קראו את המאמר Full Third-Party Cookie Blocking and More בבלוג של 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 בדפדפן שמגובה על ידי מערכת הקבצים הפרטית של המקור מוסבר איך להשתמש בו.

סיכום

הימים של נפח אחסון מוגבל ובקשות מהמשתמשים לאחסן עוד ועוד נתונים חלפו. אתרים יכולים לאחסן את כל המשאבים והנתונים שהם צריכים כדי לפעול. באמצעות 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.