Skip to content

Commit

Permalink
Reorganize report-delivery algorithms to prevent certain race conditi…
Browse files Browse the repository at this point in the history
…ons (#1105)

We remove reports from the cache prior to sending them to ensure that reports that are truly pending (and therefore subject to modification or deletion for various reasons) are not mistaken for ones that are in the process of being sent. This also allows us to remove the separate fields for original report time and report time, as while a report is in the report cache, its report time is never modified.
  • Loading branch information
apasel422 authored Nov 15, 2023
1 parent 278d07b commit ed340d6
Showing 1 changed file with 28 additions and 40 deletions.
68 changes: 28 additions & 40 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -861,10 +861,6 @@ An attribution report is a [=struct=] with the following items:
:: A [=suitable origin=].
: <dfn>report time</dfn>
:: A [=moment=].
: <dfn>original report time</dfn>
:: A [=moment=].
: <dfn>delivered</dfn> (default false)
:: A [=boolean=].
: <dfn>report ID</dfn>
:: A [=string=].
: <dfn>source debug key</dfn>
Expand Down Expand Up @@ -2141,8 +2137,7 @@ a [=trigger state=] |triggerState|:
1. Let |fakeReport| be the result of running [=obtain an event-level report=] with |source|,
|fakeTrigger|, and |fakeConfig|.
1. [=Assert=]: |fakeReport| is not null.
1. [=Assert=]: |fakeReport|'s [=event-level report/original report time=] and
|fakeReport|'s [=event-level report/report time=] are both equal to
1. [=Assert=]: |fakeReport|'s [=event-level report/report time=] is equal to
|triggerState|'s [=trigger state/report window=]'s [=report window/end=].
1. Return |fakeReport|.

Expand Down Expand Up @@ -2727,7 +2722,7 @@ To <dfn>maybe replace event-level report</dfn> given an [=attribution source=]
|sourceToAttribute|'s [=attribution source/max number of event-level reports=].
1. If |sourceToAttribute|'s [=attribution source/number of event-level reports=] is less than
|sourceToAttribute|'s [=attribution source/max number of event-level reports=], return "<code>[=event-level-report-replacement result/add-new-report=]</code>".
1. Let |matchingReports| be a new [=list=] whose elements are all the elements in the [=event-level report cache=] whose [=event-level report/original report time=] and [=event-level report/source identifier=] are equal to |report|'s, [=list/sorted in ascending order=] using [=event-level report/is lower-priority than=].
1. Let |matchingReports| be a new [=list=] whose elements are all the elements in the [=event-level report cache=] whose [=event-level report/report time=] and [=event-level report/source identifier=] are equal to |report|'s, [=list/sorted in ascending order=] using [=event-level report/is lower-priority than=].
1. If |matchingReports| [=list/is empty=]:
1. Set |sourceToAttribute|'s [=attribution source/event-level attributable=] value to false.
1. Return "<code>[=event-level-report-replacement result/drop-new-report-none-to-replace=]</code>".
Expand Down Expand Up @@ -3077,8 +3072,6 @@ To <dfn>obtain an event-level report</dfn> given an [=attribution source=] |sour
:: |source|'s [=attribution source/attribution destinations=].
: [=event-level report/report time=]
:: |reportTime|
: [=event-level report/original report time=]
:: |reportTime|
: [=event-level report/trigger priority=]
:: |config|'s [=event-level trigger configuration/priority=].
: [=event-level report/trigger time=]
Expand Down Expand Up @@ -3113,8 +3106,6 @@ an [=attribution trigger=] |trigger|:
:: |trigger|'s [=attribution trigger/attribution destination=].
: [=aggregatable report/source time=]
:: |source|'s [=attribution source/source time=].
: [=aggregatable report/original report time=]
:: |reportTime|.
: [=aggregatable report/report time=]
:: |reportTime|.
: [=aggregatable report/report id=]
Expand Down Expand Up @@ -3147,8 +3138,6 @@ To <dfn>obtain a null report</dfn> given an [=attribution trigger=] |trigger| an
:: |trigger|'s [=attribution trigger/attribution destination=]
: [=aggregatable report/source time=]
:: |sourceTime|
: [=aggregatable report/original report time=]
:: |reportTime|
: [=aggregatable report/report time=]
:: |reportTime|
: [=aggregatable report/report id=]
Expand Down Expand Up @@ -3246,22 +3235,30 @@ Issue: Specify this in terms of <a href="https://html.spec.whatwg.org/multipage/

# Report delivery # {#report-delivery}

The user agent MUST periodically [=set/iterate=] over its [=event-level report cache=] and
[=aggregatable report cache=] and run [=queue a report for delivery=] on each item.
The user agent MUST periodically run [=queue reports for delivery=] on the
[=event-level report cache=] and [=aggregatable report cache=].

To <dfn>queue reports for delivery</dfn> given a [=set=] of
[=attribution reports=] |cache|, run the following steps:

To <dfn>queue a report for delivery</dfn> given an [=attribution report=] |report|, run the following steps [=in parallel=]:
1. [=set/iterate|For each=] |report| of |cache|:
1. If |report|'s [=attribution report/report time=] is greater than the
[=current wall time=], [=iteration/continue=].
1. [=set/Remove=] |report| from |cache|.

1. If |report|'s [=attribution report/delivered=] value is true, return.
1. Set |report|'s [=attribution report/delivered=] value to true.
1. If |report|'s [=attribution report/report time=] is less than [=current wall time=], add an [=implementation-defined=] random non-negative [=duration=] to |report|'s [=attribution report/report time=].
Note: In order to support sending, waiting, and retries across various
forms of interruption, including shutdown, the user agent may need to
persist reports that are in the process of being sent in some other
storage.
1. Run the following steps [=in parallel=]:
1. Wait an [=implementation-defined=] random non-negative [=duration=].

Note: On startup, it is possible the user agent will need to send many reports whose report times passed while the browser was
closed. Adding random delay prevents temporal joining of reports from different [=attribution source/source origin=]s.
1. Wait until [=current wall time=] is equal to |report|'s [=attribution report/report time=].
1. Optionally, wait a further [=implementation-defined=] [=duration=].
Note: On startup, it is possible the user agent will need to send many reports whose report times passed while the browser was
closed. Adding random delay prevents temporal joining of reports from different [=attribution source/source origin=]s.
1. Optionally, wait a further [=implementation-defined=] [=duration=].

Note: This is intended to allow user agents to optimize device resource usage.
1. Run [=attempt to deliver a report=] with |report|.
Note: This is intended to allow user agents to optimize device resource usage.
1. Run [=attempt to deliver a report=] with |report|.

<h3 id="encode-integer">Encode an unsigned k-bit integer</h3>

Expand Down Expand Up @@ -3294,7 +3291,7 @@ of running the following steps:
: "`reporting_origin`"
:: |reportingOrigin|, [=serialization of an origin|serialized=]
: "`scheduled_report_time`"
:: |report|'s [=aggregatable report/original report time=] in seconds since the UNIX epoch, [=serialize an integer|serialized=]
:: |report|'s [=aggregatable report/report time=] in seconds since the UNIX epoch, [=serialize an integer|serialized=]
: "`version`"
:: A [=string=], API version.

Expand Down Expand Up @@ -3416,7 +3413,7 @@ To <dfn>obtain an event-level report body</dfn> given an [=attribution report=]
: "`report_id`"
:: |report|'s [=event-level report/report ID=]
: "`scheduled_report_time`"
:: |report|'s [=event-level report/original report time=] in seconds since the UNIX epoch, [=serialize an integer|serialized=]
:: |report|'s [=event-level report/report time=] in seconds since the UNIX epoch, [=serialize an integer|serialized=]

1. If |report|'s [=event-level report/source debug key=] is not null, [=map/set=]
|data|["`source_debug_key`"] to |report|'s [=event-level report/source debug key=],
Expand Down Expand Up @@ -3573,26 +3570,17 @@ To <dfn>generate attribution report headers</dfn> given an [=attribution report=

This algorithm constructs a [=request=] and attempts to deliver it to a [=suitable origin=].

To <dfn>remove a report from the cache</dfn> given an [=attribution report=] |report|:

1. If |report| is an:
<dl class="switch">
<dt>[=event-level report=]</dt>
<dd>[=Queue a task=] to [=list/remove=] |report| from the [=event-level report cache=].</dd>

<dt>[=aggregatable report=]</dt>
<dd>[=Queue a task=] to [=list/remove=] |report| from the [=aggregatable report cache=].</dd>
</dl>

To <dfn>attempt to deliver a report</dfn> given an [=attribution report=] |report|, run the following steps:

1. [=Assert=]: Neither the [=event-level report cache=] nor the
[=aggregatable report cache=] [=set/contains=] |report|.
1. The user-agent MAY ignore the report; if so, return.
1. Let |url| be the result of executing [=generate an attribution report URL=] on |report|.
1. Let |data| be the result of executing [=serialize an attribution report=] on |report|.
1. If |data| is an error, run [=remove a report from the cache=] with |report| and return.
1. If |data| is an error, return.
1. Let |headers| be the result of executing [=generate attribution report headers=].
1. Let |request| be the result of executing [=create a report request=] on |url|, |data|, and |headers|.
1. [=Queue a task=] to [=fetch=] |request| with [=fetch/processResponse=] being [=remove a report from the cache=] with |report|.
1. [=Queue a task=] to [=fetch=] |request|.

Issue(220): This fetch should use a network partition key for an opaque origin.

Expand Down

0 comments on commit ed340d6

Please sign in to comment.