|
| 1 | +# Ping Uploading |
| 2 | + |
| 3 | +This document describes how Glean.js handles ping uploading. |
| 4 | + |
| 5 | +It is not a straight line from the moment a ping is [submitted](https://mozilla.github.io/glean/book/reference/pings/index.html#submit) |
| 6 | +to the moment it is uploaded to the telemetry servers. Even though the aim is that submission will |
| 7 | +trigger upload immediatelly, multiple factors need to be accounted for that may delay upload such as: |
| 8 | +rate limitations, network errors and user interactions e.g. a user closing the application before |
| 9 | +Glean is able to upload a ping. |
| 10 | + |
| 11 | +There are three main internal structures that handle ping upload orchestration and resiliency |
| 12 | +inside the Glean.js codebase. These are: the `PingsDatabase`, the `PingUploadManager` and the |
| 13 | +`PingUploadWorker`. |
| 14 | + |
| 15 | +## `PingsDatabase` |
| 16 | + |
| 17 | +The `PingsDatabase`, as the name suggests, is a database for storing pings. It is different from the |
| 18 | +`MetricsDatabase` and `EventsDatabase`, in the sense that it contains the ping data _after collection_ |
| 19 | +i.e. it contains full JSON ping payloads instead of isolated metric data. |
| 20 | + |
| 21 | +As soon as a new ping is [collected](https://mozilla.github.io/glean/book/appendix/glossary.html#submission) |
| 22 | +it is persisted on the `PingsDatabase`. |
| 23 | + |
| 24 | +The `PingsDatabase` is an observable class. Whenever a new ping is recorded, observers are notified. |
| 25 | +When this class is initialized it scans the underlying database for any pending pings |
| 26 | +from the previous run. Observers are also notified of each ping found while scanning. |
| 27 | + |
| 28 | +## `PingUploadManager` |
| 29 | + |
| 30 | +The `PingUploadManager` is responsible for managing a queue of pings to upload and for dealing with |
| 31 | +upload responses. It is also responsible for applying limitations to ping uploads. It observes the |
| 32 | +`PingsDatabase`. Whenever it is notified of a new ping, that ping is added to the queue and |
| 33 | +the `PingUploadWorker` is started. |
| 34 | + |
| 35 | +The `PingUploadManager` exposes the `getUploadTask` and `processPingUploadResponse` APIs |
| 36 | +to be consumed by the `PingUploadWorker`. |
| 37 | + |
| 38 | +### `getUploadTask` |
| 39 | + |
| 40 | +<img align="right" width="450" height="500" src="../_assets/getUploadTask.png"> |
| 41 | + |
| 42 | +The `getUploadTask` API will return three possible tasks: |
| 43 | + |
| 44 | +- The `Upload_UploadTask`, which signals that there are still pings waiting for upload in queue |
| 45 | + and it contains the payload of the oldest ping in the queue. |
| 46 | +- The `Wait_UploadTask`, which signals that the worker has reached the rate limits for this API and should |
| 47 | + wait before calling it again. It contains an amount of milliseconds to wait before requesting new tasks. |
| 48 | +- The `Done_UploadTask`, which signals that worker is done and should stop asking for new tasks. |
| 49 | +This task is prompted either by the queue being emtpy or by upload limitations being hit. |
| 50 | + |
| 51 | +This function applies the [ping rate limitations](https://mozilla.github.io/glean/book/user/pings/index.html?highlight=client_info#rate-limiting) |
| 52 | +and guards against upload worker infinite loops e.g. when upload attempts return too many recoverable |
| 53 | +failures in a row and pings are re-enqueued and retried in a row, which can happen in case of |
| 54 | +lack of internet connection for example. |
| 55 | + |
| 56 | +### `processPingUploadResponse` |
| 57 | + |
| 58 | +The `processPingUploadResponse` API will process the result of an upload attempt. There are |
| 59 | +three possible outcomes of an upload attempt. |
| 60 | + |
| 61 | +1. **Success**: ping was uploaded succesfully and server returned status `200 OK`. |
| 62 | +2. **Unrecoverable failure**: there was error assembling a ping request or the server returned a |
| 63 | +status in the 4XX range. |
| 64 | +3. **Recoverable failure**: there was an error sending a request to the server e.g. for lack of |
| 65 | +internet connection, or the server returned an error status outside of the 4XX range. |
| 66 | + |
| 67 | +For results 1. and 2. the `processPingUploadResponse` API will process the result |
| 68 | +and delete the related ping from the pings database. Glean will not retry uploading these pings. |
| 69 | + |
| 70 | +For result 3. the ping will be re-enqueued and upload will be retried immediatelly or later. |
| 71 | +The number or recoverable errors is also increased in this case. |
| 72 | + |
| 73 | +## `PingUploadWorker` |
| 74 | + |
| 75 | +The `PingUploadWorker` is responsible for uploading pings to the telemetry server. It is |
| 76 | +also responsible for assembling the ping request by gzipping the request body (if possible) |
| 77 | +and attaching the default headers to it. |
| 78 | + |
| 79 | +The `PingUploadWorker` is initialized on an idle state. Once it's `work` API is called it will begin an |
| 80 | +asynchronous job to ask for tasks by calling the `getUploadTask` API. When it gets an `Upload_UploadTask` |
| 81 | +task, it will attempt to upload the ping and will call the `processPingUploadResponse` API with the |
| 82 | +result of each attempt. When it gets a `Wait_UploadTask` or a `Done_UploadTask` it will go back to |
| 83 | +it's idle state. |
| 84 | + |
| 85 | +The `PingUploadWorker` loop will always process one upload task at a time. If the `work` API is |
| 86 | +called while there is already ongoing work, no new jobs are started and a new task is only requested |
| 87 | +once the processing of the result of the previous task is complete. |
| 88 | + |
| 89 | +> **Note**: The diagram on ["Upload Mechanism"](https://mozilla.github.io/glean/dev/core/internal/upload.html#upload-task-api) page |
| 90 | +> illustrates nicely how the `PingUploadWorker` interacts with the `PingUploadManager` APIs -- |
| 91 | +> if you replace the "Glean core" column with `PingUploadManager` and the "Glean wrapper" column |
| 92 | +> with `PingUploadWorker`. |
0 commit comments