Skip to content

Commit 1cb0adc

Browse files
authored
Merge pull request #1713 from rosahbruno/1839955-architecture-doc-updates
2 parents a273e67 + f2c1a0f commit 1cb0adc

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed

.dictionary

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,5 @@ uuid
7575
virtualenv
7676
webext
7777
webpack
78+
async
79+
queueing

ARCHITECTURE.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,23 @@ When data is submitted, the Glean SDK is responsible for assembling the correct
3939
storage. Each metric can have different [lifetimes](https://mozilla.github.io/glean/book/user/metrics/adding-new-metrics.html#a-lifetime-example)
4040
and the SDK will manage its storage so that data does not remain in storage after it's lifetime is expired.
4141

42-
The Glean SDK tries to do all of this is the least disruptive way possible to users. All of the
43-
SDKs tasks are queued and executed asynchronously. The APIs exposed by the Glean SDK will only do
42+
The Glean SDK tries to do all of this is the least disruptive way possible to users. There are two separate
43+
implementations for the SDK based on the platform: async (QT, node, web extensions) and sync (browser). The implementation
44+
is set inside of Glean itself and is not configurable by the user.
45+
46+
### async (Web Extensions, QT, Node)
47+
48+
All of the SDKs tasks are queued and executed asynchronously. The APIs exposed by the Glean SDK will only do
4449
the en-queuing of tasks, a quick synchronous operation. Internally, the Glean SDK will handle the
4550
queued tasks asynchronously, catching any errors thrown along the way so that Glean never
4651
crashes a users application.
4752

53+
### sync (Browser)
54+
55+
All of the SDKs tasks are executed synchronously, immediately as they are called. The APIs exposed by Glean
56+
are the same as the async implementation, but without queueing of any tasks. Errors will be caught without
57+
ever crashing the application.
58+
4859
## Code Map
4960

5061
The Glean JavaScript SDK source code lives under the `glean/src` folder.
@@ -116,6 +127,13 @@ the `platform/` module contains implementations of identical interfaces in diffe
116127
This allows the pattern of only importing the necessary implementation of these modules on each platform.
117128
It also makes testing easier, because the exact same suite of tests can be run for each of the platform-specific implementations,
118129
thus guaranteeing that each module works exactly the same on all platforms.
130+
131+
The storage module varies for each platform. The storage mechanism used by each platform is as follows:
132+
- `web` - [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)
133+
- `webext` - [`storage`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage)
134+
- `QT` - [`QtQuick.LocalStorage`](https://doc.qt.io/qt-6/qtquick-localstorage-qmlmodule.html)
135+
- `Node` - None, everything is stored in memory
136+
119137
### `plugins/`
120138

121139
The `plugins/` folder contains the Glean.js' plugins code.
@@ -127,3 +145,15 @@ exposed through the `@mozilla/glean/plugins/*` entry point.
127145

128146
This file is what gets executed when a user that has installed Glean through npm invokes the `glean`
129147
command. It serves as a wrapper to call `glean_parser` commands more easily from JavaScript projects.
148+
149+
### `async`/`sync` files
150+
151+
There are certain places where we need different implementations for internal services based on the
152+
platform. In these instances, the service will have its own folder with the following folder structure:
153+
154+
```
155+
├── service/
156+
├──── async.ts
157+
├──── shared.ts # Shared base class defining all available methods AND any reusable helper functions
158+
└──── sync.ts
159+
```

docs/guides/adding_a_new_metric_type.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,38 @@ This API's design should have been discussed and decided upon during the metric
106106

107107
Still, metric type classes will always have at least one recording function and one testing function.
108108

109+
The Glean file generation is very particular with which files get bundled. This means
110+
that the implementation for both `async` and `sync` metric recording exists in the same file. Each
111+
metric file will have a set of marks `/// <something> ///` to denote which type of function should
112+
live in that block. The common marks are: `SHARED`, `ASYNC`, `SYNC`, and `TESTING`.
113+
Each function that lives under `SHARED` needs to conditionally check if the current platform is
114+
synchronous.
115+
116+
For example:
117+
118+
```ts
119+
/// SHARED ///
120+
function set(value: string): void {
121+
if (Context.isPlatformSync()) {
122+
this.setSync(value);
123+
} else {
124+
this.setAsync(value);
125+
}
126+
}
127+
128+
/// ASYNC ///
129+
function setAsync(value: string): void {
130+
Context.dispatcher.launch(async () => {
131+
// Transform and record the metric.
132+
});
133+
}
134+
135+
/// SYNC ///
136+
function setSync(value: string): void {
137+
// Transform and record the metric.
138+
}
139+
```
140+
109141
> **Note** The `type` property on the `InternalMetricType` subclass is a constant. It will be used
110142
> to determine in which section of the ping the recorded metrics for this type should be placed.
111143
> It's value is the name of the section for this metric type on the ping payload.
@@ -116,6 +148,8 @@ Still, metric type classes will always have at least one recording function and
116148

117149
_Functions that call Glean.js' database and store concrete values of a metric type._
118150

151+
##### async
152+
119153
Database calls are all asynchronous, but Glean.js' external API must **never** return promises.
120154
Therefore, Glean.js has an internal dispatcher. Asynchronous tasks are dispatched and the dispatcher
121155
will guarantee that they are executed in order without the user having to worry about
@@ -141,6 +175,25 @@ function set(value: string): void {
141175
}
142176
```
143177

178+
##### sync
179+
180+
Database calls are synchronous and nothing is sent to the dispatcher. You do not need to wrap
181+
the call inside of any callback, you can call it directly. You do need to cast the `metricsDatabase`
182+
to its synchronous type when calling.
183+
184+
```ts
185+
function set(value: string): void {
186+
// !IMPORTANT! Always check whether or not metrics should be recorded before recording.
187+
//
188+
// Metrics must not be recorded in case: upload is disabled or the metric is expired.
189+
if (!this.shouldRecord()) {
190+
return;
191+
}
192+
193+
(Glean.metricsDatabase as MetricsDatabaseSync).record(this, value);
194+
}
195+
```
196+
144197
#### Testing functions
145198

146199
_Functions that allow users to check what was recorded for the current metric type instance._

0 commit comments

Comments
 (0)