2017-03-28 06:34:14 +08:00
|
|
|
import {ipcRenderer} from 'electron'
|
2017-04-05 05:38:54 +08:00
|
|
|
import {IdentityStore, AccountStore, Actions, NylasAPI, NylasAPIRequest} from 'nylas-exports'
|
2017-03-28 06:34:14 +08:00
|
|
|
|
|
|
|
const CHECK_HEALTH_INTERVAL = 5 * 60 * 1000;
|
|
|
|
|
|
|
|
class SyncHealthChecker {
|
|
|
|
constructor() {
|
|
|
|
this._lastSyncActivity = null
|
|
|
|
this._interval = null
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
if (this._interval) {
|
|
|
|
console.warn('SyncHealthChecker has already been started')
|
|
|
|
} else {
|
|
|
|
this._interval = setInterval(this._checkSyncHealth, CHECK_HEALTH_INTERVAL)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stop() {
|
|
|
|
clearInterval(this._interval)
|
|
|
|
this._interval = null
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a separate function so the request can be manipulated in the specs
|
|
|
|
_buildRequest = () => {
|
|
|
|
return new NylasAPIRequest({
|
|
|
|
api: NylasAPI,
|
|
|
|
options: {
|
|
|
|
accountId: AccountStore.accounts()[0].id,
|
|
|
|
path: `/health`,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_checkSyncHealth = async () => {
|
|
|
|
try {
|
2017-04-05 05:38:54 +08:00
|
|
|
if (!IdentityStore.identity()) {
|
|
|
|
return
|
|
|
|
}
|
2017-03-28 06:34:14 +08:00
|
|
|
const request = this._buildRequest()
|
|
|
|
const response = await request.run()
|
|
|
|
this._lastSyncActivity = response
|
|
|
|
} catch (err) {
|
|
|
|
if (/ECONNREFUSED/i.test(err.toString())) {
|
|
|
|
this._onWorkerWindowUnavailable()
|
|
|
|
} else {
|
|
|
|
err.message = `Error checking sync health: ${err.message}`
|
|
|
|
NylasEnv.reportError(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_onWorkerWindowUnavailable() {
|
|
|
|
let extraData = {};
|
|
|
|
|
|
|
|
// Extract data that we want to report. We'll report the entire
|
|
|
|
// _lastSyncActivity object, but it'll probably be useful if we can segment
|
|
|
|
// by the data in the oldest or newest entry, so we report those as
|
|
|
|
// individual values too.
|
|
|
|
const lastActivityEntries = Object.entries(this._lastSyncActivity || {})
|
|
|
|
if (lastActivityEntries.length > 0) {
|
|
|
|
const times = lastActivityEntries.map((entry) => entry[1].time)
|
|
|
|
const now = Date.now()
|
|
|
|
|
|
|
|
const maxTime = Math.max(...times)
|
|
|
|
const mostRecentEntry = lastActivityEntries.find((entry) => entry[1].time === maxTime)
|
|
|
|
const [mostRecentActivityAccountId, {
|
|
|
|
activity: mostRecentActivity,
|
|
|
|
time: mostRecentActivityTime,
|
|
|
|
}] = mostRecentEntry;
|
|
|
|
const mostRecentDuration = now - mostRecentActivityTime
|
|
|
|
|
|
|
|
const minTime = Math.min(...times)
|
|
|
|
const leastRecentEntry = lastActivityEntries.find((entry) => entry[1].time === minTime)
|
|
|
|
const [leastRecentActivityAccountId, {
|
|
|
|
activity: leastRecentActivity,
|
|
|
|
time: leastRecentActivityTime,
|
|
|
|
}] = leastRecentEntry;
|
|
|
|
const leastRecentDuration = now - leastRecentActivityTime
|
|
|
|
|
|
|
|
extraData = {
|
|
|
|
mostRecentActivity,
|
|
|
|
mostRecentActivityTime,
|
|
|
|
mostRecentActivityAccountId,
|
|
|
|
mostRecentDuration,
|
|
|
|
leastRecentActivity,
|
|
|
|
leastRecentActivityTime,
|
|
|
|
leastRecentActivityAccountId,
|
|
|
|
leastRecentDuration,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NylasEnv.reportError(new Error('Worker window was unavailable'), {
|
|
|
|
// This information isn't as useful in Sentry, but include it here until
|
|
|
|
// the data is actually sent to Mixpanel. (See the TODO below)
|
|
|
|
lastActivityPerAccount: this._lastSyncActivity,
|
|
|
|
...extraData,
|
|
|
|
})
|
|
|
|
|
|
|
|
// TODO: This doesn't make it to Mixpanel because our analytics process
|
|
|
|
// lives in the worker window. We should move analytics to the main process.
|
|
|
|
// https://phab.nylas.com/T8029
|
|
|
|
Actions.recordUserEvent('Worker Window Unavailable', {
|
|
|
|
lastActivityPerAccount: this._lastSyncActivity,
|
|
|
|
...extraData,
|
|
|
|
})
|
|
|
|
|
|
|
|
console.log(`Detected worker window was unavailable. Restarting it.`, this._lastSyncActivity)
|
|
|
|
ipcRenderer.send('ensure-worker-window')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default new SyncHealthChecker()
|