From c971ed03e2f16c4cbff59e6f53ab2473be50cd5c Mon Sep 17 00:00:00 2001 From: Mark Hahnenberg Date: Fri, 10 Mar 2017 12:06:23 -0800 Subject: [PATCH] [client-sync] Shim sequelize to timeout after 1 minute Summary: Sequelize can sometimes return promises that will never resolve or reject. We can wrap the promises we get back from sequelize in a bluebird promise which gives us the ability to timeout these abandoned promises. This way, we can track down issues in sequelize as well as unblocking stuck sync loops. Test Plan: Run locally, verify that timeouts occur Reviewers: juan, evan, spang Reviewed By: evan Differential Revision: https://phab.nylas.com/D4192 --- packages/client-sync/main.es6 | 3 ++ .../client-sync/src/shared/shim-sequelize.es6 | 37 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 packages/client-sync/src/shared/shim-sequelize.es6 diff --git a/packages/client-sync/main.es6 b/packages/client-sync/main.es6 index a657f814c..6571e3a5c 100644 --- a/packages/client-sync/main.es6 +++ b/packages/client-sync/main.es6 @@ -1,8 +1,11 @@ /* eslint global-require: 0 */ +import Sequelize from 'sequelize'; // eslint-disable-line import {ComponentRegistry} from 'nylas-exports' import {createLogger} from './src/shared/logger' +import shimSequelize from './src/shared/shim-sequelize' export function activate() { + shimSequelize(Sequelize); global.Logger = createLogger() require('./src/local-api'); require('./src/local-sync-worker'); diff --git a/packages/client-sync/src/shared/shim-sequelize.es6 b/packages/client-sync/src/shared/shim-sequelize.es6 new file mode 100644 index 000000000..d50678f56 --- /dev/null +++ b/packages/client-sync/src/shared/shim-sequelize.es6 @@ -0,0 +1,37 @@ +const _ = require('underscore'); + +const DEFAULT_SEQUELIZE_SHIM_TIMEOUT = 60 * 1000; // 1 min + +const shimObject = (obj) => { + Object.keys(obj).forEach(key => { + // Skip internal methods. + if (key.startsWith('_')) { + return; + } + + const prop = obj[key]; + // Only patch methods. + if (!_.isFunction(prop)) { + return; + } + + obj[key] = function(...args) { // eslint-disable-line + const result = prop.call(this, ...args); + if (result && _.isFunction(result.then)) { + return new Promise(async (resolve, reject) => { + try { + resolve(await result); + } catch (err) { + reject(err); + } + }).timeout(DEFAULT_SEQUELIZE_SHIM_TIMEOUT, `${key} timed out`); + } + return result; + }; + }); +}; + +export default function shimSequelize(Sequelize) { + shimObject(Sequelize.Model); + shimObject(Sequelize.Instance.prototype); +}