Skip to content
This repository was archived by the owner on Feb 2, 2021. It is now read-only.

Fix EMFILE error when running on Android #1036

Merged
merged 1 commit into from
Dec 20, 2017

Conversation

rosen-vladimirov
Copy link
Collaborator

During running on Android, CLI tries to take smart decision which files to upload. This is done by checking the shasum of all project files and comparing it with the latest one uploaded to device. However, in huge projects, this leads to error with code EMFILE as the OS have limits for opened file handles.
This happens as CLI executes the operation simultaneously for all files. In order to fix this behavior, execute the actions by chunks (currently set to 100).
Introduce new method in helpers to execute this action and add tests for it.

Fixes NativeScript/nativescript-angular#1079 and NativeScript/nativescript-cli#3268

@@ -673,7 +673,7 @@ declare module Mobile {
/**
* Computes the shasums of localToDevicePaths and changes the content of hash file on device
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these docs are no longer correct

helpers.ts Outdated
if (_.isArray(initialData)) {
const chunks = _.chunk(initialData, chunkSize);
for (const chunk of chunks) {
await Promise.all(_.map(chunk, element => elementAction(element)));
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe pass the index of the element as second argument?

@@ -53,26 +54,26 @@ export class AndroidDeviceFileSystem implements Mobile.IDeviceFileSystem {
public async transferFiles(deviceAppData: Mobile.IDeviceAppData, localToDevicePaths: Mobile.ILocalToDevicePathData[]): Promise<void> {
// TODO: Do not start all promises simultaneously as this leads to error EMFILE on Windows for too many opened files.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this TODO is implemented with current changes, so remove it

await executeActionByChunks<Mobile.ILocalToDevicePathData>(localToDevicePaths, 100, action);

const dirsChmodAction = async (directoryToChmod: string) => {
await this.adb.executeShellCommand(["chmod", "0777", directoryToChmod]);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be safely returned instead of awaited:

const dirsChmodAction = (directoryToChmod: string) => this.adb.executeShellCommand(["chmod", "0777", directoryToChmod]);

await this.adb.executeShellCommand(["chmod", "0777", directoryToChmod]);
};

await executeActionByChunks<string>(_.uniq(directoriesToChmod), 100, dirsChmodAction);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract 100 in a constant at least in this file

@@ -15,6 +15,60 @@ describe("helpers", () => {
assert.deepEqual(actualResult, testData.expectedResult, `For input ${testData.input}, the expected result is: ${testData.expectedResult}, but actual result is: ${actualResult}.`);
};

describe("executeActionByChunks", () => {
const verify = (initialDataValues: any[], handledElements: any[], element: any, chunkSize: number) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

verify should be renamed


const initialDataValues = _.values(initialData);
const handledElements: number[] = [];
const chunkSize = 2;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move chunkSize outside of the it and reuse it in the two tests

const action = async (localToDevicePathData: Mobile.ILocalToDevicePathData) => {
const localPath = localToDevicePathData.getLocalPath();
const stats = this.$fs.getFsStats(localPath);
if (stats.isFile()) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code seems like a duplicate of part of the code from updateHashes in android-device-hash-service. Consider extracting a new method that executes the common part.


const handledElements: number[] = [];
const chunkSize = 2;
return helpers.executeActionByChunks(initialData, chunkSize, async element => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to mark this as async

const initialDataValues = _.values(initialData);
const handledElements: number[] = [];
const chunkSize = 2;
return helpers.executeActionByChunks(initialData, chunkSize, async (element, key) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to mark this as async

During running on Android, CLI tries to take smart decision which files to upload. This is done by checking the shasum of all project files and comparing it with the latest one uploaded to device. However, in huge projects, this leads to error with code EMFILE as the OS have limits for opened file handles.
This happens as CLI executes the operation simultaneously for all files. In order to fix this behavior, execute the actions by chunks (currently set to 100).
Introduce new method in helpers to execute this action and add tests for it.
@rosen-vladimirov rosen-vladimirov force-pushed the vladimirov/fix-emfile-error branch from e490f05 to 0d1a466 Compare December 20, 2017 07:29
Copy link
Contributor

@Mitko-Kerezov Mitko-Kerezov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems legit

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Angular 5] Error: ENFILE: file table overflow
2 participants