<commit_before>const {
  SchedulerUtils,
  IMAPConnection,
  PubsubConnector,
  DatabaseConnector,
  MessageTypes,
  Errors,
  PromiseUtils,
} = require('nylas-core');
const {
  jsonError,
} = require('./sync-utils')

const {CLAIM_DURATION} = SchedulerUtils;

const FetchFolderList = require('./imap/fetch-folder-list')
const FetchMessagesInFolder = require('./imap/fetch-messages-in-folder')
const SyncbackTaskFactory = require('./syncback-task-factory')


class SyncWorker {
  constructor(account, db, onExpired) {
    this._db = db;
    this._conn = null;
    this._account = account;
    this._startTime = Date.now();
    this._lastSyncTime = null;
    this._onExpired = onExpired;
    this._logger = global.Logger.forAccount(account)

    this._syncTimer = null;
    this._destroyed = false;

    this.syncNow({reason: 'Initial'});

    this._onMessage = this._onMessage.bind(this);
    this._listener = PubsubConnector.observeAccount(account.id).subscribe(this._onMessage)
  }

  cleanup() {
    clearTimeout(this._syncTimer);
    this._syncTimer = null;
    this._destroyed = true;
    this._listener.dispose();
    this.closeConnection()
  }

  closeConnection() {
    if (this._conn) {
      this._conn.end();
    }
  }

  _onMessage(msg) {
    const {type} = JSON.parse(msg);
    switch (type) {
      case MessageTypes.ACCOUNT_CREATED:
        // No other processing currently required for account creation
        break;
      case MessageTypes.ACCOUNT_UPDATED:
        this._onAccountUpdated();
        break;
      case MessageTypes.ACCOUNT_DELETED:
        this.cleanup();
        this._onExpired();
        break;
      case MessageTypes.SYNCBACK_REQUESTED:
        this.syncNow({reason: 'Syncback Action Queued'});
        break;
      default:
        this._logger.error({message: msg}, 'SyncWorker: Invalid message')
    }
  }

  _onAccountUpdated() {
    if (!this.isWaitingForNextSync()) {
      return;
    }
    this._getAccount()
    .then((account) => {
      this._account = account;
      this.syncNow({reason: 'Account Modification'});
    })
    .catch((err) => {
      this._logger.error(err, 'SyncWorker: Error getting account for update')
    })
  }

  _onConnectionIdleUpdate() {
    if (!this.isWaitingForNextSync()) {
      return;
    }
    this.syncNow({reason: 'IMAP IDLE Fired'});
  }

  _getAccount() {
    return DatabaseConnector.forShared().then(({Account}) =>
      Account.find({where: {id: this._account.id}})
    );
  }

  _getIdleFolder() {
    return this._db.Folder.find({where: {role: ['all', 'inbox']}})
  }

  ensureConnection() {
    if (this._conn) {
      return this._conn.connect();
    }
    const settings = this._account.connectionSettings;<commit_msg>Fix accounts syncing in a hard loop due to redis loopback<commit_after>const {
  SchedulerUtils,
  IMAPConnection,
  PubsubConnector,
  DatabaseConnector,
  MessageTypes,
  Errors,
  PromiseUtils,
} = require('nylas-core');
const {
  jsonError,
} = require('./sync-utils')

const {CLAIM_DURATION} = SchedulerUtils;

const FetchFolderList = require('./imap/fetch-folder-list')
const FetchMessagesInFolder = require('./imap/fetch-messages-in-folder')
const SyncbackTaskFactory = require('./syncback-task-factory')


class SyncWorker {
  constructor(account, db, onExpired) {
    this._db = db;
    this._conn = null;
    this._account = account;
    this._startTime = Date.now();
    this._lastSyncTime = null;
    this._onExpired = onExpired;
    this._logger = global.Logger.forAccount(account)

    this._syncTimer = null;
    this._destroyed = false;

    this.syncNow({reason: 'Initial'});

    this._onMessage = this._onMessage.bind(this);
    this._listener = PubsubConnector.observeAccount(account.id).subscribe(this._onMessage)
  }

  cleanup() {
    clearTimeout(this._syncTimer);
    this._syncTimer = null;
    this._destroyed = true;
    this._listener.dispose();
    this.closeConnection()
  }

  closeConnection() {
    if (this._conn) {
      this._conn.end();
    }
  }

  _onMessage(msg) {
    const {type} = JSON.parse(msg);
    switch (type) {
      case MessageTypes.ACCOUNT_CREATED:
        // No other processing currently required for account creation
        break;
      case MessageTypes.ACCOUNT_UPDATED:
        this._onAccountUpdated();
        break;
      case MessageTypes.ACCOUNT_DELETED:
        this.cleanup();
        this._onExpired();
        break;
      case MessageTypes.SYNCBACK_REQUESTED:
        this.syncNow({reason: 'Syncback Action Queued'});
        break;
      default:
        this._logger.error({message: msg}, 'SyncWorker: Invalid message')
    }
  }

  _onAccountUpdated() {
    const syncingNow = !this.isWaitingForNextSync()
    const syncingJustFinished = (Date.now() - this._lastSyncTime < 5000);

    if (syncingNow || syncingJustFinished) {
      return;
    }

    this._getAccount().then((account) => {
      this._account = account;
      this.syncNow({reason: 'Account Modification'});
    })
    .catch((err) => {
      this._logger.error(err, 'SyncWorker: Error getting account for update')
    })
  }

  _onConnectionIdleUpdate() {
    if (!this.isWaitingForNextSync()) {
      return;
    }
    this.syncNow({reason: 'IMAP IDLE Fired'});
  }

  _getAccount() {
    return DatabaseConnector.forShared().then(({Account}) =>
      Account.find({where: {id: this._account.id}})
    );
  }

  _getIdleFolder() {
    return this._db.Folder.find({where: {role: ['all', 'inbox']}})
  }

  ensureConnection() {
    if (this._conn) {
      return this._conn.connect();
    }
    const settings = this._account.connectionSettings;
{'fraction_non_alphanumeric': 0.13486959628438727, 'fraction_numerical': 0.0007145409074669524, 'mean_word_length': 3.0601885424220447, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2212617', 'n_tokens_mistral': 1778, 'n_tokens_neox': 1754, 'n_words': 319}