@inlang/sdk 0.34.3 → 0.34.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,124 @@
1
+ import { type NodeishFilesystem } from "@lix-js/fs"
2
+ import type { NodeishStats } from "@lix-js/fs"
3
+ import _debug from "debug"
4
+ const debug = _debug("sdk:acquireFileLock")
5
+
6
+ const maxRetries = 10
7
+ const nProbes = 50
8
+ const probeInterval = 100
9
+ export async function acquireFileLock(
10
+ fs: NodeishFilesystem,
11
+ lockDirPath: string,
12
+ lockOrigin: string,
13
+ tryCount: number = 0
14
+ ): Promise<number> {
15
+ if (tryCount > maxRetries) {
16
+ throw new Error(lockOrigin + " exceeded maximum Retries (5) to acquire lockfile " + tryCount)
17
+ }
18
+
19
+ try {
20
+ debug(lockOrigin + " tries to acquire a lockfile Retry Nr.: " + tryCount)
21
+ await fs.mkdir(lockDirPath)
22
+ const stats = await fs.stat(lockDirPath)
23
+ debug(lockOrigin + " acquired a lockfile Retry Nr.: " + tryCount)
24
+ return stats.mtimeMs
25
+ } catch (error: any) {
26
+ if (error.code !== "EEXIST") {
27
+ // we only expect the error that the file exists already (locked by other process)
28
+ throw error
29
+ }
30
+ }
31
+
32
+ let currentLockTime: number
33
+
34
+ try {
35
+ const stats = await fs.stat(lockDirPath)
36
+ currentLockTime = stats.mtimeMs
37
+ } catch (fstatError: any) {
38
+ if (fstatError.code === "ENOENT") {
39
+ // lock file seems to be gone :) - lets try again
40
+ debug(lockOrigin + " tryCount++ lock file seems to be gone :) - lets try again " + tryCount)
41
+ return acquireFileLock(fs, lockDirPath, lockOrigin, tryCount + 1)
42
+ }
43
+ throw fstatError
44
+ }
45
+ debug(
46
+ lockOrigin +
47
+ " tries to acquire a lockfile - lock currently in use... starting probe phase " +
48
+ tryCount
49
+ )
50
+
51
+ return new Promise((resolve, reject) => {
52
+ let probeCounts = 0
53
+ const scheduleProbationTimeout = () => {
54
+ setTimeout(async () => {
55
+ probeCounts += 1
56
+ let lockFileStats: undefined | NodeishStats = undefined
57
+ try {
58
+ debug(
59
+ lockOrigin + " tries to acquire a lockfile - check if the lock is free now " + tryCount
60
+ )
61
+
62
+ // alright lets give it another try
63
+ lockFileStats = await fs.stat(lockDirPath)
64
+ } catch (fstatError: any) {
65
+ if (fstatError.code === "ENOENT") {
66
+ debug(
67
+ lockOrigin +
68
+ " tryCount++ in Promise - tries to acquire a lockfile - lock file seems to be free now - try to acquire " +
69
+ tryCount
70
+ )
71
+ const lock = acquireFileLock(fs, lockDirPath, lockOrigin, tryCount + 1)
72
+ return resolve(lock)
73
+ }
74
+ return reject(fstatError)
75
+ }
76
+
77
+ // still the same locker! -
78
+ if (lockFileStats.mtimeMs === currentLockTime) {
79
+ if (probeCounts >= nProbes) {
80
+ // ok maximum lock time ran up (we waitetd nProbes * probeInterval) - we consider the lock to be stale
81
+ debug(
82
+ lockOrigin +
83
+ " tries to acquire a lockfile - lock not free - but stale lets drop it" +
84
+ tryCount
85
+ )
86
+ try {
87
+ await fs.rmdir(lockDirPath)
88
+ } catch (rmLockError: any) {
89
+ if (rmLockError.code === "ENOENT") {
90
+ // lock already gone?
91
+ // Option 1: The "stale process" decided to get rid of it
92
+ // Option 2: Another process acquiring the lock and detected a stale one as well
93
+ }
94
+ return reject(rmLockError)
95
+ }
96
+ try {
97
+ debug(
98
+ lockOrigin +
99
+ " tryCount++ same locker - try to acquire again after removing stale lock " +
100
+ tryCount
101
+ )
102
+ const lock = await acquireFileLock(fs, lockDirPath, lockOrigin, tryCount + 1)
103
+ return resolve(lock)
104
+ } catch (lockAquireException) {
105
+ return reject(lockAquireException)
106
+ }
107
+ } else {
108
+ // lets schedule a new probation
109
+ return scheduleProbationTimeout()
110
+ }
111
+ } else {
112
+ try {
113
+ debug(lockOrigin + " tryCount++ different locker - try to acquire again " + tryCount)
114
+ const lock = await acquireFileLock(fs, lockDirPath, lockOrigin, tryCount + 1)
115
+ return resolve(lock)
116
+ } catch (error) {
117
+ return reject(error)
118
+ }
119
+ }
120
+ }, probeInterval)
121
+ }
122
+ scheduleProbationTimeout()
123
+ })
124
+ }
@@ -0,0 +1,28 @@
1
+ import { type NodeishFilesystem } from "@lix-js/fs"
2
+ import _debug from "debug"
3
+ const debug = _debug("sdk:releaseLock")
4
+
5
+ export async function releaseLock(
6
+ fs: NodeishFilesystem,
7
+ lockDirPath: string,
8
+ lockOrigin: string,
9
+ lockTime: number
10
+ ) {
11
+ debug(lockOrigin + " releasing the lock ")
12
+ try {
13
+ const stats = await fs.stat(lockDirPath)
14
+ if (stats.mtimeMs === lockTime) {
15
+ // this can be corrupt as welll since the last getStat and the current a modification could have occured :-/
16
+ await fs.rmdir(lockDirPath)
17
+ }
18
+ } catch (statError: any) {
19
+ debug(lockOrigin + " couldn't release the lock")
20
+ if (statError.code === "ENOENT") {
21
+ // ok seeks like the log was released by someone else
22
+ debug(lockOrigin + " WARNING - the lock was released by a different process")
23
+ return
24
+ }
25
+ debug(statError)
26
+ throw statError
27
+ }
28
+ }