מדריך למתחילים: הטמעת נפח אחסון משותף וצבירה פרטית

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

קהל היעד: ספקי טכנולוגיות פרסום וספקי מדידה.

Shared Storage API

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

האחסון השיתופי מוגבל למקור ההקשר (מבצע הקריאה של sharedStorage).

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

הפעלת Shared Storage

טכנאי הפרסום יכולים לכתוב ב-Shared Storage באמצעות JavaScript או כותרות תגובה. הקריאה מ-Shared Storage מתבצעת רק בסביבת JavaScript מבודדת שנקראת worklet.

  • באמצעות JavaScript טכנאי הפרסום יכולים לבצע פונקציות ספציפיות של אחסון משותף, כמו הגדרת ערכים, הוספה של ערכים ומחיקה של ערכים מחוץ ל-worklet של JavaScript. עם זאת, פונקציות כמו קריאת נתונים מאחסון משותף וביצוע צבירה פרטית צריכות להתבצע באמצעות רכיב JavaScript. שיטות שאפשר להשתמש בהן מחוץ ל-worklet של JavaScript מפורטות בקטע Proposed API Surface – Outside the worklet.

    שיטות שמשמשות ב-worklet במהלך פעולה מפורטות בקטע Proposed API Surface – In the worklet.

  • שימוש בכותרות תגובה

    בדומה ל-JavaScript, אפשר לבצע רק פונקציות ספציפיות באמצעות כותרות התגובה, כמו הגדרה, הוספה וחיסול של ערכים ב-Shared Storage. כדי לעבוד עם אחסון משותף בכותרת תגובה, צריך לכלול את Shared-Storage-Writable: ?1 בכותרת הבקשה.

    כדי להתחיל בקשה מהלקוח, מריצים את הקוד הבא, בהתאם לשיטה שבחרתם:

    • שימוש ב-fetch()

      fetch("https://a.example/path/for/updates", {sharedStorageWritable: true});
      
    • שימוש בתג iframe או img

      <iframe src="https://a.example/path/for/updates" sharedstoragewritable></iframe>
      
    • שימוש במאפיין IDL עם תג iframe או img

      let iframe = document.getElementById("my-iframe");
      iframe.sharedStorageWritable = true;
      iframe.src = "https://a.example/path/for/updates";
      

מידע נוסף זמין במאמר אחסון משותף: כותרות תגובה.

כתיבת נתונים בנפח האחסון המשותף

כדי לכתוב ב-Shared Storage, צריך להפעיל את sharedStorage.set() מתוך worklet של JavaScript או מחוץ לו. אם הקריאה מתבצעת מחוץ ל-worklet, הנתונים נכתבים למקור של הקשר הגלישה שממנו בוצעה הקריאה. אם הקריאה מתבצעת מתוך ה-worklet, הנתונים נכתבים למקור של הקשר הגלישה שבו ה-worklet נטען. תאריך התפוגה של המפתחות שמוגדרים הוא 30 יום מתאריך העדכון האחרון.

השדה ignoreIfPresent הוא אופציונלי. אם הוא קיים ומוגדר כ-true, המפתח לא מתעדכן אם הוא כבר קיים. תפוגת התוקף של המפתח מתחדשת ל-30 יום ממועד הקריאה ל-set(), גם אם המפתח לא מתעדכן.

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

  • שימוש ב-JavaScript

    מחוץ למודול ה-worklet:

    window.sharedStorage.set('myKey', 'myValue1', { ignoreIfPresent: true });
    // Shared Storage: {'myKey': 'myValue1'}
    window.sharedStorage.set('myKey', 'myValue2', { ignoreIfPresent: true });
    // Shared Storage: {'myKey': 'myValue1'}
    window.sharedStorage.set('myKey', 'myValue2', { ignoreIfPresent: false });
    // Shared Storage: {'myKey': 'myValue2'}
    

    באופן דומה, בתוך הווידג'ט:

    sharedStorage.set('myKey', 'myValue1', { ignoreIfPresent: true });
    
  • שימוש בכותרות תגובה

    אפשר גם לכתוב ב-Shared Storage באמצעות כותרות תגובה. כדי לעשות זאת, צריך להשתמש ב-Shared-Storage-Write בכותרת התגובה יחד עם הפקודות הבאות:

    Shared-Storage-Write : set;key="myKey";value="myValue";ignore_if_present
    
    Shared-Storage-Write : set;key="myKey";value="myValue";ignore_if_present=?0
    

    אפשר להפריד בין כמה פריטים באמצעות פסיק, ואפשר לשלב בין set,‏ append, ‏ delete ו-clear.

    Shared-Storage-Write :
    set;key="hello";value="world";ignore_if_present, set;key="good";value="bye"
    

הוספת ערך

אפשר לצרף ערך למפתח קיים באמצעות שיטת ה-append. אם המפתח לא קיים, קריאה ל-append() יוצרת את המפתח ומגדירה את הערך. אפשר לעשות זאת באמצעות JavaScript או כותרות תגובה.

  • שימוש ב-JavaScript

    כדי לעדכן ערכים של מפתחות קיימים, משתמשים ב-sharedStorage.append() מתוך ה-worklet או מחוץ לו.

    window.sharedStorage.append('myKey', 'myValue1');
    // Shared Storage: {'myKey': 'myValue1'}
    window.sharedStorage.append('myKey', 'myValue2');
    // Shared Storage: {'myKey': 'myValue1myValue2'}
    window.sharedStorage.append('anotherKey', 'hello');
    // Shared Storage: {'myKey': 'myValue1myValue2', 'anotherKey': 'hello'}
    

    כדי להוסיף בתוך ה-worklet:

    sharedStorage.append('myKey', 'myValue1');
    
  • שימוש בכותרות תגובה

    בדומה להגדרת ערך באחסון משותף, אפשר להשתמש ב-Shared-Storage-Write בכותרת התגובה כדי להעביר את הצמד מפתח/ערך.

    Shared-Storage-Write : append;key="myKey";value="myValue2"
    

עדכון ערכים בכמות גדולה

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

אפשר להגדיר את הנעילה באמצעות JavaScript או כותרת תגובה:

  • שימוש ב-JavaScript

    שיטות ה-JavaScript הזמינות שאפשר להשתמש בהן עם batchUpdate() כוללות:

    • SharedStorageSetMethod(): כתיבת צמד מפתח/ערך ב-Shared Storage.
    • SharedStorageAppendMethod(): הוספת ערך למפתח קיים ב-Shared Storage.
    • SharedStorageDeleteMethod(): מחיקה של צמד מפתח/ערך מ-Shared Storage.
    • SharedStorageClearMethod(): מחיקת כל המפתחות באחסון המשותף.
    sharedStorage.batchUpdate([
    new SharedStorageSetMethod('keyOne', 'valueOne'),
    new SharedStorageAppendMethod('keyTwo', 'valueTwo'),
    new SharedStorageDeleteMethod('keyThree'),
    new SharedStorageClearMethod()
    ]);
    
  • שימוש בכותרות תגובה

    Shared-Storage-Write : batchUpdate;methods="set;key=keyOne;value=valueOne, append;key=keyTwo;value=valueTwo,delete;key=keyThree,clear"
    

קריאה מנפח אחסון משותף

אפשר לקרוא מאחסון משותף רק מתוך worklet.

await sharedStorage.get('mykey');

המקור של הקשר הגלישה שממנו מודול ה-worklet נטען קובע את האחסון המשותף של מי שיקרא.

מחיקת פריטים מנפח האחסון המשותף

אפשר לבצע מחיקה של נתונים מאחסון משותף באמצעות JavaScript, מתוך ה-worklet או מחוץ לו, או באמצעות כותרות תגובה עם delete(). כדי למחוק את כל המפתחות בבת אחת, משתמשים ב-clear() מכל אחד מהם.

  • שימוש ב-JavaScript

    כדי למחוק מהאחסון השיתופי מחוץ ל-worklet:

    window.sharedStorage.delete('myKey');
    

    כדי למחוק מ-Shared Storage מתוך ה-worklet:

    sharedStorage.delete('myKey');
    

    כדי למחוק את כל המפתחות בבת אחת מחוץ ל-worklet:

    window.sharedStorage.clear();
    

    כדי למחוק את כל המפתחות בבת אחת מתוך ה-worklet:

    sharedStorage.clear();
    
  • שימוש בכותרות תגובה

    כדי למחוק ערכים באמצעות כותרות תגובה, אפשר גם להשתמש ב-Shared-Storage-Write בכותרת התגובה כדי להעביר את המפתח שרוצים למחוק.

    delete;key="myKey"
    

    כדי למחוק את כל המפתחות באמצעות כותרות התגובה:

    clear;
    

קריאת קבוצות של תחומי עניין של Protected Audience מאחסון משותף

אפשר לקרוא את קבוצות העניין של Protected Audience מ-worklet של אחסון שיתופי. שיטת interestGroups() מחזירה מערך של אובייקטים מסוג StorageInterestGroup, כולל המאפיינים AuctionInterestGroup ו-GenerateBidInterestGroup.

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

async function analyzeInterestGroups() {
  const interestGroups = await interestGroups();
  numIGs = interestGroups.length;
  maxBidCountIG = interestGroups.reduce((max, cur) => { return cur.bidCount > max.bidCount ? cur : max; }, interestGroups[0]);
  console.log("The IG that bid the most has name " + maxBidCountIG.name);
}

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

אפשרויות

כל השיטות של Shared Storage שתומכות בשינויים תומכות באובייקט אפשרויות אופציונלי בתור הארגומנט האחרון.

withLock

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

אפשר להשתמש באפשרות withLock בשיטות הבאות של מודификаторים של אחסון משותף:

  • הוגדר
  • append
  • delete
  • נקה
  • חבילת עדכונים

אפשר להגדיר את הנעילה באמצעות JavaScript או כותרת תגובה:

  • שימוש ב-JavaScript

    sharedStorage.set('myKey', 'myValue', { withLock: 'myResource' });
    
  • שימוש בכותרות תגובה

    Shared-Storage-Write : set;key="myKey";value="myValue";with_lock="myResource"
    

המנעולים של האחסון המשותף מחולקים למחיצות לפי מקור הנתונים. המנעולים עצמאיים מכל המנעולים שהתקבלו באמצעות השיטה request()‎ של LockManager, גם אם הם בהקשר של window וגם אם הם בהקשר של worker. עם זאת, הם חולקים את אותו היקף כמו המנעולים שהתקבלו באמצעות request() בהקשר של SharedStorageWorklet.

השיטה request() מאפשרת אפשרויות הגדרה שונות, אבל מנעולים שנוצרים באחסון השיתופי תמיד פועלים לפי הגדרות ברירת המחדל הבאות:

  • mode: "exclusive": לא ניתן להחזיק בו-זמנית מנעולים אחרים עם אותו שם.
  • steal: false: מנעולים קיימים עם אותו שם לא משוחררים כדי לאפשר טיפול בבקשות אחרות.
  • ifAvailable: false: הבקשות ממתינות ללא הגבלת זמן עד שהנעילה תהיה זמינה.
מתי להשתמש ב-withLock

מנעולים שימושיים בתרחישים שבהם יכולים לפעול כמה וורקלטס בו-זמנית (למשל, כמה וורקלטס בדף או כמה וורקלטס בכרטיסיות שונות), שכל אחד מהם בודק את אותם נתונים. במקרה כזה, מומלץ לעטוף את קוד ה-worklet הרלוונטי בנעילה כדי לוודא שרק worklet אחד מעבד דוחות בכל פעם.

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

סדר הנעילות

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

לדוגמה:

// This line might pause until the lock is available.
sharedStorage.set('keyOne', 'valueOne', { withLock: 'resource-lock' });

// This line will run right away, even if the first one is still waiting.
sharedStorage.set('keyOne', 'valueTwo');
דוגמה לשינוי של כמה מפתחות

בדוגמה הזו נעשה שימוש בנעילת (lock) כדי לוודא שפעולת הקריאה והמחיקה בתוך ה-worklet מתבצעות יחד, ומונעת הפרעה מחוץ ל-worklet.

בדוגמה הבאה של modify-multiple-keys.js, מגדירים ערכים חדשים ל-keyOne ול-keyTwo באמצעות modify-lock, ולאחר מכן מבצעים את הפעולה modify-multiple-keys מה-worklet:

// modify-multiple-keys.js
sharedStorage.batchUpdate([
    new SharedStorageSetMethod('keyOne', calculateValueFor('keyOne')),
    new SharedStorageSetMethod('keyTwo', calculateValueFor('keyTwo'))
], { withLock: 'modify-lock' });

const modifyWorklet = await sharedStorage.createWorklet('modify-multiple-keys-worklet.js');
await modifyWorklet.run('modify-multiple-keys');

לאחר מכן, בתוך modify-multiple-keys-worklet.js אפשר לבקש את המנעול באמצעות navigator.locks.request() כדי לקרוא ולשנות את המפתחות לפי הצורך.

// modify-multiple-keys-worklet.js
class ModifyMultipleKeysOperation {
  async run(data) {
    await navigator.locks.request('modify-lock', async (lock) => {
      const value1 = await sharedStorage.get('keyOne');
      const value2 = await sharedStorage.get('keyTwo');

      // Do something with `value1` and `value2` here.

      await sharedStorage.delete('keyOne');
      await sharedStorage.delete('keyTwo');
    });
  }
}
register('modify-multiple-keys', ModifyMultipleKeysOperation);

מעבר בין הקשרים

נתוני האחסון המשותף נכתבים במקור (לדוגמה, https://example.adtech.com) של הקשר הגלישה שממנו הגיעה הקריאה.

כשאתם מעמיסים את הקוד של הצד השלישי באמצעות תג <script>, הקוד מופעל בהקשר הגלישה של המטמיע. לכן, כשהקוד של הצד השלישי קורא ל-sharedStorage.set(), הנתונים נכתבים ב-Shared Storage של המטמיע. כשאתם טוענים את הקוד של הצד השלישי בתוך iframe, הקוד מקבל הקשר חדש של גלישה והמקור שלו הוא המקור של ה-iframe. לכן, הקריאה ל-sharedStorage.set() שמתבצעת מה-iframe מאחסנת את הנתונים באחסון המשותף של מקור ה-iframe.

נתונים שנאספים ישירות ממשתמשים

אם בדף מאינטראקציה ישירה (First-Party) מוטמע קוד JavaScript של צד שלישי שמפעיל את sharedStorage.set() או את sharedStorage.delete(), צימוד המפתח/ערך נשמר בהקשר של האינטראקציה הישירה.

נתונים שמאוחסנים בדף מהדומיין הנוכחי עם JavaScript מוטמע של צד שלישי.

הקשר של צד שלישי

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

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

Private Aggregation API

כדי למדוד נתונים שניתן לצבור אותם שנשמרים באחסון שיתופי, אפשר להשתמש ב-Private Aggregation API.

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

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

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

privateAggregation.contributeToHistogram({
  bucket: BigInt(myBucket),
  value: parseInt(myBucketValue)
});

הפעלת אחסון משותף וצבירה פרטית

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

כדי לשנות את התנהגות ברירת המחדל, מגדירים את המאפיין dataOrigin בזמן הקריאה ל-createWorklet.

  • dataOrigin: "context-origin": (ברירת המחדל) הנתונים נשמרים באחסון המשותף של המקור של הקשר הגלישה שמפעיל את הקריאה.
  • dataOrigin: "script-origin": הנתונים מאוחסנים באחסון המשותף של המקור של סקריפט ה-worklet. חשוב לזכור: כדי להפעיל את המצב הזה, צריך להביע הסכמה.
sharedStorage.createWorklet(scriptUrl, {dataOrigin: "script-origin"});

כדי להביע הסכמה, כשמשתמשים ב-"script-origin", נקודת הקצה של הסקריפט תצטרך להגיב עם הכותרת Shared-Storage-Cross-Origin-Worklet-Allowed. חשוב לזכור שצריך להפעיל את CORS לבקשות ממקורות שונים.

Shared-Storage-Cross-Origin-Worklet-Allowed : ?1

שימוש ב-iframe חוצה-מקורות

צריך iframe כדי להפעיל את ה-worklet של האחסון המשותף.

ב-iframe של המודעה, מעמיסים את מודול ה-worklet באמצעות קריאה ל-addModule(). כדי להריץ את השיטה שרשומה בקובץ ה-worklet‏ sharedStorageWorklet.js, צריך לבצע קריאה ל-sharedStorage.run() באותו JavaScript של iframe של המודעה.

const sharedStorageWorklet = await window.sharedStorage.createWorklet(
  'https://any-origin.example/modules/sharedStorageWorklet.js'
);
await sharedStorageWorklet.run('shared-storage-report', {
  data: { campaignId: '1234' },
});

בסקריפט של ה-worklet, צריך ליצור מחלקה עם שיטת run אסינכררונית וregister אותה כדי להריץ אותה ב-iframe של המודעה. בתוך sharedStorageWorklet.js:

class SharedStorageReportOperation {
  async run(data) {
    // Other code goes here.
    bucket = getBucket(...);
    value = getValue(...);
    privateAggregation.contributeToHistogram({
      bucket,
      value
    });
  }
}
register('shared-storage-report', SharedStorageReportOperation);

שימוש בבקשת CORS

נפח אחסון משותף וצבירה פרטית מאפשרים ליצור רכיבי worklet ממקורות שונים בלי צורך במסגרות iframe ממקורות שונים.

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

async function crossOriginCall() {
  const privateAggregationWorklet = await sharedStorage.createWorklet(
    'https://cross-origin.example/js/worklet.js',
    { dataOrigin: 'script-origin' }
  );
  await privateAggregationWorklet.run('pa-worklet');
}
crossOriginCall();

נקודת הקצה של JavaScript ממקורות שונים תצטרך להשיב עם הכותרות Shared-Storage-Cross-Origin-Worklet-Allowed ולציין ש-CORS מופעל בבקשה.

Shared-Storage-Cross-Origin-Worklet-Allowed : ?1

ל-worklets שנוצרו באמצעות createWorklet() יהיו selectURL ו-run(). אי אפשר להשתמש ב-addModule() לצורך כך.

class CrossOriginWorklet {
  async run(data){
    // Other code goes here.
    bucket = getBucket(...);
    value = getValue(...);
    privateAggregation.contributeToHistogram({
      bucket,
      value
    });
  }
}

השלבים הבאים

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

אחרי שתתמצאו בממשקי ה-API, תוכלו להתחיל לאסוף את הדוחות. הם נשלחים כבקשת POST לנקודות הקצה הבאות כ-JSON בגוף הבקשה.

  • דוחות ניפוי באגים – context-origin/.well-known/private-aggregation/debug/report-shared-storage
  • דוחות – context-origin/.well-known/private-aggregation/report-shared-storage

אחרי שהדוחות נאספים, אפשר לבדוק אותם באמצעות כלי הבדיקה המקומי או להגדיר את Trusted Execution Environment for Aggregation Service כדי לקבל את הדוחות המצטברים.

שיתוף משוב

אתם יכולים לשלוח משוב על ממשקי ה-API ועל המסמכים ב-GitHub.