This sounds very similar to what I ran into with Firefox Health Report.
There is some discussion in
https://bugzilla.mozilla.org/show_bug.cgi?id=722648 and
https://bugzilla.mozilla.org/show_bug.cgi?id=850712.
Generally speaking, you have a queued async operation or set of async
operations that need to complete. However, profile-before-change is
observed before or during those operations. There is a race between
xpcom-shutdown and those async operations finishing.
FHR's solution to this problem is to initiate the shutdown procedure
early during shutdown. We chose quit-application. If this shutdown
sequence (which consists of waiting for async operations initiated or
queued before shutdown started) has not completed by the time
profile-before-change (the last observer before the profile goes away)
is observed, FHR creates a nested event loop during the
profile-before-change handler. This delays shutdown. But it eliminates
race conditions and prevents data corruption. Code at
https://hg.mozilla.org/mozilla-central/file/26ab72bfa9df/services/healthreport/healthreporter.jsm#l445
Performance *really* doesn't like this solution because it can delay
shutdown. However, the last I checked Telemetry, we only had 121 reports
on Nightly and Aurora of this occurring in the past month. This is
because we only create a nested event loop if shutdown hasn't finished
by profile-before-change and unless a major FHR operation was initiated
just before shutdown, there will be no work to do except close the
database handle. If we were buffering data and persistently flushing,
we'd likely be spinning much more since we'd be more likely to have
queued I/O.
I'll conclude by saying that every scenario is different. I highly
recommend you connect with someone on the Perf team for a tailored
recommendation.