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

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

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

מדיניות ההוצאה מהזיכרון (eviction) עבור שיטת 'האפשרות הטובה ביותר' היא:

  • דפדפנים מבוססי-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 המודרניים שמבוססים על הבטחות, הוא מבוסס על אירועים. עטיפות של הבטחות כמו 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.