Add retries around android device watching

Summary: Retry up to 5 times with exponential backoff.

Reviewed By: passy

Differential Revision: D13138244

fbshipit-source-id: 94c68b87aa281cfc3239b0bb8bdd75a2e0d7edc7
This commit is contained in:
John Knox
2018-11-22 06:38:09 -08:00
committed by Facebook Github Bot
parent 9e6a86923a
commit ca1f0202d7
4 changed files with 122 additions and 58 deletions

20
flow-typed/npm/promise-retry_v1.1.x.js vendored Normal file
View File

@@ -0,0 +1,20 @@
// flow-typed signature: 83c416de68c62add9a5f5c1178d383bb
// flow-typed version: 15b5072ad2/promise-retry_v1.1.x/flow_>=v0.45.x
type RetryFn = (err?: Error) => void;
type Options = {|
retries?: number,
factor?: number,
minTimeout?: number,
maxTimeout?: number,
randomize?: boolean,
|};
declare module 'promise-retry' {
declare export type RetryOptions = Options;
declare module.exports: <T>(
handler: (retry: RetryFn, retryNumber: Number) => Promise<T>,
options?: Options
) => Promise<T>;
}

View File

@@ -71,6 +71,7 @@
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"openssl-wrapper": "^0.3.4", "openssl-wrapper": "^0.3.4",
"promise-retry": "^1.1.1",
"prop-types": "^15.6.0", "prop-types": "^15.6.0",
"react": "16", "react": "16",
"react-color": "^2.11.7", "react-color": "^2.11.7",

View File

@@ -7,6 +7,7 @@
import AndroidDevice from '../devices/AndroidDevice'; import AndroidDevice from '../devices/AndroidDevice';
import child_process from 'child_process'; import child_process from 'child_process';
import promiseRetry from 'promise-retry';
import type {Store} from '../reducers/index.js'; import type {Store} from '../reducers/index.js';
import type BaseDevice from '../devices/BaseDevice'; import type BaseDevice from '../devices/BaseDevice';
import type Logger from '../fb-stubs/Logger.js'; import type Logger from '../fb-stubs/Logger.js';
@@ -57,10 +58,28 @@ export default (store: Store, logger: Logger) => {
// Using this client before adb server is started up will cause failures. // Using this client before adb server is started up will cause failures.
// this gets around this by waiting for listDevices first, which ensures // this gets around this by waiting for listDevices first, which ensures
// the server is up and running before allowing any other operations. // the server is up and running before allowing any other operations.
const clientPromise = (() => {
function createClient() {
const unsafeClient = adb.createClient(); const unsafeClient = adb.createClient();
return unsafeClient.listDevices().then(() => unsafeClient); return promiseRetry(
})(); (retry, number) => {
return unsafeClient
.listDevices()
.then(() => {
return unsafeClient;
})
.catch(e => {
console.warn(`Failed to start adb client. Retrying. ${e.message}`);
retry(e);
});
},
{
minTimeout: 200,
retries: 5,
},
);
}
const clientPromise = createClient();
const watchAndroidDevices = () => { const watchAndroidDevices = () => {
// get emulators // get emulators
@@ -79,71 +98,77 @@ export default (store: Store, logger: Logger) => {
}, },
); );
clientPromise.then(client => { clientPromise
client .then(client => {
.trackDevices() client
.then(tracker => { .trackDevices()
tracker.on('error', err => { .then(tracker => {
if (err.message === 'Connection closed') { tracker.on('error', err => {
// adb server has shutdown, remove all android devices if (err.message === 'Connection closed') {
const {connections} = store.getState(); // adb server has shutdown, remove all android devices
const deviceIDsToRemove: Array< const {connections} = store.getState();
string, const deviceIDsToRemove: Array<
> = connections.devices string,
.filter((device: BaseDevice) => device instanceof AndroidDevice) > = connections.devices
.map((device: BaseDevice) => device.serial); .filter(
(device: BaseDevice) => device instanceof AndroidDevice,
)
.map((device: BaseDevice) => device.serial);
store.dispatch({ store.dispatch({
type: 'UNREGISTER_DEVICES', type: 'UNREGISTER_DEVICES',
payload: new Set(deviceIDsToRemove), payload: new Set(deviceIDsToRemove),
}); });
console.error('adb server was shutdown'); console.error('adb server was shutdown');
setTimeout(watchAndroidDevices, 500); setTimeout(watchAndroidDevices, 500);
} else { } else {
throw err; throw err;
} }
}); });
tracker.on('add', async device => { tracker.on('add', async device => {
if (device.type !== 'offline') { if (device.type !== 'offline') {
const androidDevice = await createDevice(client, device); const androidDevice = await createDevice(client, device);
store.dispatch({ store.dispatch({
type: 'REGISTER_DEVICE', type: 'REGISTER_DEVICE',
payload: androidDevice, payload: androidDevice,
}); });
} }
}); });
tracker.on('change', async device => { tracker.on('change', async device => {
if (device.type === 'offline') { if (device.type === 'offline') {
store.dispatch({
type: 'UNREGISTER_DEVICES',
payload: new Set([device.id]),
});
} else {
const androidDevice = await createDevice(client, device);
store.dispatch({
type: 'REGISTER_DEVICE',
payload: androidDevice,
});
}
});
tracker.on('remove', device => {
store.dispatch({ store.dispatch({
type: 'UNREGISTER_DEVICES', type: 'UNREGISTER_DEVICES',
payload: new Set([device.id]), payload: new Set([device.id]),
}); });
});
})
.catch(err => {
if (err.code === 'ECONNREFUSED') {
// adb server isn't running
} else { } else {
const androidDevice = await createDevice(client, device); throw err;
store.dispatch({
type: 'REGISTER_DEVICE',
payload: androidDevice,
});
} }
}); });
})
tracker.on('remove', device => { .catch(e => {
store.dispatch({ console.error(`Failed to watch for android devices: ${e.message}`);
type: 'UNREGISTER_DEVICES', });
payload: new Set([device.id]),
});
});
})
.catch(err => {
if (err.code === 'ECONNREFUSED') {
// adb server isn't running
} else {
throw err;
}
});
});
}; };
watchAndroidDevices(); watchAndroidDevices();

View File

@@ -2273,6 +2273,11 @@ env-paths@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0"
err-code@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960"
integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=
error-ex@^1.2.0: error-ex@^1.2.0:
version "1.3.1" version "1.3.1"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
@@ -5137,6 +5142,14 @@ progress@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
promise-retry@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-1.1.1.tgz#6739e968e3051da20ce6497fb2b50f6911df3d6d"
integrity sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=
dependencies:
err-code "^1.0.0"
retry "^0.10.0"
promise@^7.1.1: promise@^7.1.1:
version "7.3.1" version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
@@ -5627,6 +5640,11 @@ ret@~0.1.10:
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
retry@^0.10.0:
version "0.10.1"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4"
integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=
rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1: rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1:
version "2.6.2" version "2.6.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"