Bug 1609734 - Swift: Adds use of overflow metric when pre-init queue overflows

This commit is contained in:
Travis Long 2020-01-21 14:55:11 -06:00
Родитель 53fb434f46
Коммит e69e55d4c0
3 изменённых файлов: 58 добавлений и 1 удалений

Просмотреть файл

@ -13,6 +13,9 @@
* iOS:
* Collections performed before initialization (preinit tasks) are now dispatched off
the main thread and not awaited during initialization.
* Added recording of `glean.error.preinit_tasks_overflow` to report when
the preinit task queue overruns, leading to data loss. See [bug
1609734](https://bugzilla.mozilla.org/show_bug.cgi?id=1609734)
# v24.1.0 (2020-01-16)

Просмотреть файл

@ -15,7 +15,7 @@ class Dispatchers {
// This struct is used for organizational purposes to keep the class constants in a single place
struct Constants {
static let logTag = "glean/Dispatchers"
static let maxQueueSize = 100
static let maxQueueSize: Int32 = 100
// This is the number of seconds that are allowed for the initial tasks queue to
// process all of the queued tasks.
@ -46,6 +46,9 @@ class Dispatchers {
// This array will hold the queued initial tasks that are launched before Glean is initialized
lazy var preInitOperations = [Operation]()
// The number of items that were added to the queue after it filled up.
var preInitTaskCount: Int32 = 0
// When true, jobs will be run synchronously
var testingMode = false
@ -83,6 +86,12 @@ class Dispatchers {
} else {
logger.error("Exceeded maximum queue size, discarding task")
}
// This value ends up in the `preinit_tasks_overflow` metric, but we
// can't record directly there, because that would only add
// the recording to an already-overflowing task queue and would be
// silently dropped.
preInitTaskCount += 1
} else {
// If we are not queuing initial tasks, we can go ahead and execute the Operation by
// adding it to the `operationQueue`
@ -176,6 +185,13 @@ class Dispatchers {
// Turn off queuing to allow for normal background execution mode
queueInitialTasks.value = false
// This must happen after `queueInitialTasks.set(false)` is run, or it
// would be added to a full task queue and be silently dropped.
if preInitTaskCount > Constants.maxQueueSize {
GleanMetrics.GleanError.preinitTasksOverflow.add(preInitTaskCount)
}
preInitTaskCount = 0
// Clear the cached operations
preInitOperations.removeAll()
}

Просмотреть файл

@ -217,4 +217,42 @@ class DispatchersTest: XCTestCase {
// Make sure counter hasn't changed
XCTAssertEqual(counter, asyncTest, "Concurrent task must be cancelled")
}
func testOverflowingTheTaskQueueRecordsTelemetry() {
Glean.shared.resetGlean(clearStores: true)
Dispatchers.shared.preInitOperations.removeAll()
Dispatchers.shared.setTestingMode(enabled: true)
Dispatchers.shared.setTaskQueuing(enabled: true)
for _ in 0 ..< (Dispatchers.Constants.maxQueueSize + 10) {
Dispatchers.shared.launchAPI {
// Do nothing
}
}
XCTAssertEqual(
Int(Dispatchers.Constants.maxQueueSize),
Dispatchers.shared.preInitOperations.count,
"Task queue must contain the maximum number of tasks"
)
XCTAssertEqual(
Dispatchers.Constants.maxQueueSize + 10,
Dispatchers.shared.preInitTaskCount,
"preInitTaskCount is correct"
)
// Resetting Glean here causes the tasks to be flushed and the
// queueing turned off, this should cause the error metric to
// be recorded so we can check it to ensure the value matches
// the expected value.
Glean.shared.resetGlean(clearStores: false)
XCTAssertEqual(
Dispatchers.Constants.maxQueueSize + 10,
try GleanMetrics.GleanError.preinitTasksOverflow.testGetValue(),
"preInitTaskCount is correct"
)
Glean.shared.resetGlean(clearStores: true)
}
}