Browse Source

Add more timeout on the sync to avoid issues

master
Gaëtan Renaudeau 7 years ago
parent
commit
3e71c48917
  1. 2
      src/config/constants.js
  2. 1
      src/config/errors.js
  3. 62
      src/helpers/libcore.js
  4. 18
      src/helpers/promise.js
  5. 4
      static/i18n/en/errors.json

2
src/config/constants.js

@ -46,7 +46,7 @@ export const SYNC_ALL_INTERVAL = 120 * 1000
export const SYNC_BOOT_DELAY = 2 * 1000 export const SYNC_BOOT_DELAY = 2 * 1000
export const SYNC_PENDING_INTERVAL = 10 * 1000 export const SYNC_PENDING_INTERVAL = 10 * 1000
export const SYNC_MAX_CONCURRENT = intFromEnv('LEDGER_SYNC_MAX_CONCURRENT', 1) export const SYNC_MAX_CONCURRENT = intFromEnv('LEDGER_SYNC_MAX_CONCURRENT', 1)
export const SYNC_TIMEOUT = intFromEnv('SYNC_TIMEOUT', 30 * 1000) export const SYNC_TIMEOUT = intFromEnv('SYNC_TIMEOUT', 60 * 1000)
// Endpoints... // Endpoints...

1
src/config/errors.js

@ -12,6 +12,7 @@ export const UserRefusedAddress = createCustomErrorClass('UserRefusedAddress')
export const WrongDeviceForAccount = createCustomErrorClass('WrongDeviceForAccount') export const WrongDeviceForAccount = createCustomErrorClass('WrongDeviceForAccount')
export const DeviceNotGenuineError = createCustomErrorClass('DeviceNotGenuine') export const DeviceNotGenuineError = createCustomErrorClass('DeviceNotGenuine')
export const DeviceGenuineSocketEarlyClose = createCustomErrorClass('DeviceGenuineSocketEarlyClose') export const DeviceGenuineSocketEarlyClose = createCustomErrorClass('DeviceGenuineSocketEarlyClose')
export const TimeoutTagged = createCustomErrorClass('TimeoutTagged')
// db stuff, no need to translate // db stuff, no need to translate
export const NoDBPathGiven = createCustomErrorClass('NoDBPathGiven') export const NoDBPathGiven = createCustomErrorClass('NoDBPathGiven')

62
src/helpers/libcore.js

@ -16,6 +16,7 @@ import { isSegwitPath, isUnsplitPath } from 'helpers/bip32'
import * as accountIdHelper from 'helpers/accountId' import * as accountIdHelper from 'helpers/accountId'
import { createCustomErrorClass, deserializeError } from './errors' import { createCustomErrorClass, deserializeError } from './errors'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from './accountName' import { getAccountPlaceholderName, getNewAccountPlaceholderName } from './accountName'
import { timeoutTagged } from './promise'
const NoAddressesFound = createCustomErrorClass('NoAddressesFound') const NoAddressesFound = createCustomErrorClass('NoAddressesFound')
@ -202,6 +203,7 @@ const coreSyncAccount = (core, account) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
const eventReceiver = createEventReceiver(core, e => { const eventReceiver = createEventReceiver(core, e => {
const code = e.getCode() const code = e.getCode()
logger.debug(`syncAccountEvent ${code}`, { type: 'libcore-sync' })
if (code === core.EVENT_CODE.UNDEFINED || code === core.EVENT_CODE.SYNCHRONIZATION_FAILED) { if (code === core.EVENT_CODE.UNDEFINED || code === core.EVENT_CODE.SYNCHRONIZATION_FAILED) {
const payload = e.getPayload() const payload = e.getPayload()
const message = ( const message = (
@ -270,7 +272,7 @@ async function scanNextAccount(props: {
const shouldSyncAccount = true // TODO: let's sync everytime. maybe in the future we can optimize. const shouldSyncAccount = true // TODO: let's sync everytime. maybe in the future we can optimize.
if (shouldSyncAccount) { if (shouldSyncAccount) {
await coreSyncAccount(core, njsAccount) await timeoutTagged('coreSyncAccount', 30000, coreSyncAccount(core, njsAccount))
} }
if (isUnsubscribed()) return [] if (isUnsubscribed()) return []
@ -325,10 +327,10 @@ async function getOrCreateWallet(
): NJSWallet { ): NJSWallet {
const pool = core.getPoolInstance() const pool = core.getPoolInstance()
try { try {
const wallet = await pool.getWallet(WALLET_IDENTIFIER) const wallet = await timeoutTagged('getWallet', 5000, pool.getWallet(WALLET_IDENTIFIER))
return wallet return wallet
} catch (err) { } catch (err) {
const currency = await pool.getCurrency(currencyId) const currency = await timeoutTagged('getCurrency', 5000, pool.getCurrency(currencyId))
const splitConfig = isUnsplit ? SPLITTED_CURRENCIES[currencyId] || null : null const splitConfig = isUnsplit ? SPLITTED_CURRENCIES[currencyId] || null : null
const coinType = splitConfig ? splitConfig.coinType : '<coin_type>' const coinType = splitConfig ? splitConfig.coinType : '<coin_type>'
const walletConfig = isSegwit const walletConfig = isSegwit
@ -342,9 +344,11 @@ async function getOrCreateWallet(
} }
: undefined : undefined
const njsWalletConfig = createWalletConfig(core, walletConfig) const njsWalletConfig = createWalletConfig(core, walletConfig)
const wallet = await core const wallet = await timeoutTagged(
.getPoolInstance() 'createWallet',
.createWallet(WALLET_IDENTIFIER, currency, njsWalletConfig) 10000,
core.getPoolInstance().createWallet(WALLET_IDENTIFIER, currency, njsWalletConfig),
)
return wallet return wallet
} }
} }
@ -368,21 +372,33 @@ async function buildAccountRaw({
core: *, core: *,
ops: NJSOperation[], ops: NJSOperation[],
}): Promise<AccountRaw> { }): Promise<AccountRaw> {
const njsBalance = await njsAccount.getBalance() const njsBalance = await timeoutTagged('getBalance', 10000, njsAccount.getBalance())
const balance = njsBalance.toLong() const balance = njsBalance.toLong()
const jsCurrency = getCryptoCurrencyById(currencyId) const jsCurrency = getCryptoCurrencyById(currencyId)
const { derivations } = await wallet.getAccountCreationInfo(accountIndex) const { derivations } = await timeoutTagged(
'getAccountCreationInfo',
10000,
wallet.getAccountCreationInfo(accountIndex),
)
const [walletPath, accountPath] = derivations const [walletPath, accountPath] = derivations
// retrieve xpub // retrieve xpub
const xpub = njsAccount.getRestoreKey() const xpub = njsAccount.getRestoreKey()
// blockHeight // blockHeight
const { height: blockHeight } = await njsAccount.getLastBlock() const { height: blockHeight } = await timeoutTagged(
'getLastBlock',
30000,
njsAccount.getLastBlock(),
)
// get a bunch of fresh addresses // get a bunch of fresh addresses
const rawAddresses = await njsAccount.getFreshPublicAddresses() const rawAddresses = await timeoutTagged(
'getFreshPublicAddresses',
10000,
njsAccount.getFreshPublicAddresses(),
)
const addresses = rawAddresses.map(njsAddress => ({ const addresses = rawAddresses.map(njsAddress => ({
str: njsAddress.toString(), str: njsAddress.toString(),
@ -500,7 +516,11 @@ export async function syncAccount({
const isUnsplit = isUnsplitPath(freshAddressPath, SPLITTED_CURRENCIES[currencyId]) const isUnsplit = isUnsplitPath(freshAddressPath, SPLITTED_CURRENCIES[currencyId])
let njsWallet let njsWallet
try { try {
njsWallet = await core.getPoolInstance().getWallet(decodedAccountId.walletName) njsWallet = await timeoutTagged(
'getWallet',
10000,
core.getPoolInstance().getWallet(decodedAccountId.walletName),
)
} catch (e) { } catch (e) {
logger.warn(`Have to reimport the account... (${e})`) logger.warn(`Have to reimport the account... (${e})`)
njsWallet = await getOrCreateWallet( njsWallet = await getOrCreateWallet(
@ -514,20 +534,28 @@ export async function syncAccount({
let njsAccount let njsAccount
try { try {
njsAccount = await njsWallet.getAccount(index) njsAccount = await timeoutTagged('getAccount', 10000, njsWallet.getAccount(index))
} catch (e) { } catch (e) {
logger.warn(`Have to recreate the account... (${e.message})`) logger.warn(`Have to recreate the account... (${e.message})`)
const extendedInfos = await njsWallet.getExtendedKeyAccountCreationInfo(index) const extendedInfos = await timeoutTagged(
'getEKACI',
10000,
njsWallet.getExtendedKeyAccountCreationInfo(index),
)
extendedInfos.extendedKeys.push(decodedAccountId.xpub) extendedInfos.extendedKeys.push(decodedAccountId.xpub)
njsAccount = await njsWallet.newAccountWithExtendedKeyInfo(extendedInfos) njsAccount = await timeoutTagged(
'newAWEKI',
10000,
njsWallet.newAccountWithExtendedKeyInfo(extendedInfos),
)
} }
const unsub = await coreSyncAccount(core, njsAccount) const unsub = await timeoutTagged('coreSyncAccount', 30000, coreSyncAccount(core, njsAccount))
unsub() unsub()
const query = njsAccount.queryOperations() const query = njsAccount.queryOperations()
const ops = await query.complete().execute() const ops = await timeoutTagged('ops', 30000, query.complete().execute())
const njsBalance = await njsAccount.getBalance() const njsBalance = await timeoutTagged('getBalance', 10000, njsAccount.getBalance())
const syncedRawAccount = await buildAccountRaw({ const syncedRawAccount = await buildAccountRaw({
njsAccount, njsAccount,

18
src/helpers/promise.js

@ -2,6 +2,7 @@
// small utilities for Promises // small utilities for Promises
import logger from 'logger' import logger from 'logger'
import { TimeoutTagged } from 'config/errors'
export const delay = (ms: number): Promise<void> => new Promise(f => setTimeout(f, ms)) export const delay = (ms: number): Promise<void> => new Promise(f => setTimeout(f, ms))
@ -65,6 +66,23 @@ export function createCancelablePolling(
return { unsubscribe, promise } return { unsubscribe, promise }
} }
export const timeoutTagged = <T>(tag: string, delay: number, promise: Promise<T>): Promise<T> =>
new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new TimeoutTagged('timeout', { tag }))
}, delay)
promise.then(
r => {
clearTimeout(timeout)
resolve(r)
},
e => {
clearTimeout(timeout)
reject(e)
},
)
})
export const promisify = (fn: any) => (...args: any) => export const promisify = (fn: any) => (...args: any) =>
new Promise((resolve, reject) => new Promise((resolve, reject) =>
fn(...args, (err: Error, res: any) => { fn(...args, (err: Error, res: any) => {

4
static/i18n/en/errors.json

@ -115,6 +115,10 @@
"title": "Oops, a time out occurred", "title": "Oops, a time out occurred",
"description": "It took too long for the server to respond." "description": "It took too long for the server to respond."
}, },
"TimeoutTagged": {
"title": "Oops, a time out occurred ({{tag}})",
"description": "It took too long for the server to respond."
},
"TransportError": { "TransportError": {
"title": "Something went wrong. Please reconnect your device.", "title": "Something went wrong. Please reconnect your device.",
"description": "{{message}}" "description": "{{message}}"

Loading…
Cancel
Save