@lmzhen/dsh-evolution-core 0.3.28 → 0.3.29

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.
Files changed (2) hide show
  1. package/lib/index.js +19 -8
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -147,16 +147,18 @@ function nodeEvolutionIo() {
147
147
  */
148
148
  const withWriteLock = async (path, task) => {
149
149
  const lock = `${path}.lock`;
150
+ let myClaim = "";
150
151
  for (let attempt = 0; attempt < 40; attempt += 1) {
151
152
  try {
152
- await writeFile(lock, String(process.pid), { flag: "wx" });
153
+ myClaim = `${process.pid}:${randomBytes(4).toString("hex")}`;
154
+ await writeFile(lock, myClaim, { flag: "wx" });
153
155
  } catch (error) {
154
156
  const code = error?.code;
155
157
  if (code !== "EEXIST" && code !== "EPERM") throw error;
156
158
  try {
157
159
  const st = await stat(lock);
158
160
  const holderContent = await readFile(lock, "utf8").catch(() => "");
159
- const holder = Number(holderContent);
161
+ const holder = Number(holderContent.split(":")[0] ?? "");
160
162
  const holderAlive = Number.isInteger(holder) && holder > 0 && isAlive(holder);
161
163
  if (holder === process.pid && pendingSelfCleanup.has(lock)) {
162
164
  if (await readFile(lock, "utf8").catch(() => "") === holderContent) {
@@ -167,15 +169,23 @@ function nodeEvolutionIo() {
167
169
  }
168
170
  continue;
169
171
  }
170
- if (Date.now() - st.mtimeMs > 1e3 && !holderAlive) {
172
+ if (Number.isInteger(holder) && holder > 0 && Date.now() - st.mtimeMs > 1e3 && !holderAlive) {
171
173
  if (await readFile(lock, "utf8").catch(() => "") === holderContent) {
172
- const takeover = `${lock}.takeover-${process.pid}-${randomBytes(6).toString("hex")}`;
174
+ const ticket = `${lock}.next`;
173
175
  try {
174
- await rename(lock, takeover);
175
- await rm(takeover, { force: true }).catch(() => {});
176
+ const ticketBody = await readFile(ticket, "utf8").catch(() => "");
177
+ const ticketHolder = Number(ticketBody.split(":")[0] ?? "");
178
+ if ((!Number.isInteger(ticketHolder) || ticketHolder <= 0 || !isAlive(ticketHolder) || Date.now() - await stat(ticket).then((s) => s.mtimeMs, () => 0) > 1e3) && ticketBody !== "") await rm(ticket, { force: true }).catch(() => {});
176
179
  } catch {}
180
+ try {
181
+ await writeFile(ticket, `${process.pid}:${randomBytes(4).toString("hex")}`, { flag: "wx" });
182
+ } catch {
183
+ continue;
184
+ }
185
+ if (await readFile(lock, "utf8").catch(() => "") === holderContent) await rm(lock, { force: true }).catch(() => {});
186
+ await rm(ticket, { force: true }).catch(() => {});
187
+ continue;
177
188
  }
178
- continue;
179
189
  }
180
190
  } catch {
181
191
  continue;
@@ -186,7 +196,7 @@ function nodeEvolutionIo() {
186
196
  try {
187
197
  return await task();
188
198
  } finally {
189
- await rm(lock, { force: true }).catch(() => {
199
+ if (await readFile(lock, "utf8").catch(() => null) === myClaim) await rm(lock, { force: true }).catch(() => {
190
200
  pendingSelfCleanup.add(lock);
191
201
  });
192
202
  }
@@ -252,6 +262,7 @@ function nodeEvolutionIo() {
252
262
  await rm(path, { force: true });
253
263
  return;
254
264
  }
265
+ if (next === current) return;
255
266
  const tmp = `${path}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
256
267
  await writeFile(tmp, next, "utf8");
257
268
  await commitTmp(tmp, path);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-core",
3
3
  "description": "Shared stores, prompts, signals and lifecycle logic for the dsh-evolution plugin family (community build)",
4
- "version": "0.3.28",
4
+ "version": "0.3.29",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },