Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add file locking mechanism #8

Merged
merged 2 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@
"devDependencies": {
"@commitlint/cli": "^18.2.0",
"@commitlint/config-conventional": "^11.0.0",
"biolink-model": "workspace:../biolink-model",
"@types/debug": "^4.1.10",
"@types/jest": "^29.5.7",
"@types/lodash": "^4.14.200",
"@types/node": "^20.8.10",
"@typescript-eslint/eslint-plugin": "^6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"biolink-model": "workspace:../biolink-model",
"eslint": "^8.53.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
Expand All @@ -59,6 +59,7 @@
"ioredis": "^5.3.2",
"ioredis-mock": "^8.9.0",
"lodash": "^4.17.21",
"proper-lockfile": "^4.1.2",
"redlock": "5.0.0-beta.2"
}
}
38 changes: 38 additions & 0 deletions src/misc.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import lockfile from "proper-lockfile";

export function toArray<Type>(input: Type | Type[]): Type[] {
if (Array.isArray(input)) {
return input;
Expand Down Expand Up @@ -76,3 +78,39 @@
reject = newReject;
});
}

export const LOCKFILE_STALENESS = {stale: 5000}; // lock expiration in milliseconds to prevent deadlocks
export const LOCKFILE_RETRY_CONFIG = {
retries: {
retries: 10,
factor: 2,
minTimeout: 100,
maxTimeout: 1000,
},
stale: LOCKFILE_STALENESS["stale"],
};

export async function lockWithActionAsync<T>(filePaths: string[], action: () => Promise<T>, debug?: (message: string) => void, lockfileRetryConfig?: any): Promise<T> {
if (process.env.NODE_ENV !== "production") {
debug(`Development mode: Skipping lockfile ${process.env.NODE_ENV}`);
const result = await action();
return result;

Check warning on line 97 in src/misc.ts

View check run for this annotation

Codecov / codecov/patch

src/misc.ts#L95-L97

Added lines #L95 - L97 were not covered by tests
}

const releases: (() => void)[] = [];

Check warning on line 100 in src/misc.ts

View check run for this annotation

Codecov / codecov/patch

src/misc.ts#L100

Added line #L100 was not covered by tests
const retryConfig = lockfileRetryConfig || LOCKFILE_RETRY_CONFIG;
try {
for (const filePath of filePaths) {
let release = await lockfile.lock(filePath, retryConfig);
releases.push(release);

Check warning on line 105 in src/misc.ts

View check run for this annotation

Codecov / codecov/patch

src/misc.ts#L102-L105

Added lines #L102 - L105 were not covered by tests
}
const result = await action();
return result;

Check warning on line 108 in src/misc.ts

View check run for this annotation

Codecov / codecov/patch

src/misc.ts#L107-L108

Added lines #L107 - L108 were not covered by tests
} catch (error) {
debug(`Lockfile error: ${error}`);

Check warning on line 110 in src/misc.ts

View check run for this annotation

Codecov / codecov/patch

src/misc.ts#L110

Added line #L110 was not covered by tests
// throw error;
} finally {
for (const release of releases)

Check warning on line 113 in src/misc.ts

View check run for this annotation

Codecov / codecov/patch

src/misc.ts#L113

Added line #L113 was not covered by tests
if (release) release();
}
}
Loading