Skip to content

Commit c787b2f

Browse files
authored
feat: specify different lockfile directory (#238)
1 parent 2955ff6 commit c787b2f

File tree

3 files changed

+44
-8
lines changed

3 files changed

+44
-8
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ The following options exist:
8282

8383
To create a compressed copy of the database in `/path/to/file.dump`, use the `dump()` method. If any data is written to the db during the dump, it is appended to the dump but most likely compressed.
8484

85+
### Changing where the lockfile is created
86+
87+
Normally, the lockfile to avoid concurrent access to the DB file is created right next to the DB file. You can change this, e.g. to put the lockfile into a `tmpfs`:
88+
```ts
89+
const db = new DB("/path/to/file", { lockfileDirectory: "/var/tmp" });
90+
```
91+
If the directory does not exist, it will be created when opening the DB.
92+
8593
### Copying and compressing the database
8694

8795
```ts
@@ -133,6 +141,9 @@ The file will be overwritten if it exists. The 2nd options argument can be used
133141
Placeholder for next release:
134142
### __WORK IN PROGRESS__
135143
-->
144+
### __WORK IN PROGRESS__
145+
* Add the ability to specify where the lockfile is created
146+
136147
### 2.1.0 (2021-06-22)
137148
* When opening the DB, recover from crashes that happened while compressing the DB
138149
* Ensure that the DB files are flushed to disk when closing or renaming files

src/lib/db.test.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,20 @@ describe("lib/db", () => {
182182
await db.close();
183183
});
184184

185+
it("also creates leading directories for the lockfiles if they don't exist", async () => {
186+
const lockfileDirectory = path.join(
187+
testFSRoot,
188+
"this/path/does/not/exist/either",
189+
);
190+
const db = new JsonlDB(path.join(testFSRoot, "lockfile"), {
191+
lockfileDirectory,
192+
});
193+
await db.open();
194+
await db.close();
195+
196+
await expect(fs.pathExists(lockfileDirectory)).resolves.toBeTrue();
197+
});
198+
185199
it("reads the file if it exists", async () => {
186200
const db = new JsonlDB(path.join(testFSRoot, "yes"));
187201
await db.open();
@@ -196,7 +210,7 @@ describe("lib/db", () => {
196210
try {
197211
await db2.open();
198212
throw new Error("it did not throw");
199-
} catch (e) {
213+
} catch (e: any) {
200214
expect(e.message).toMatch(/Failed to lock/i);
201215
}
202216

@@ -240,7 +254,7 @@ describe("lib/db", () => {
240254
try {
241255
await db.open();
242256
throw new Error("it did not throw");
243-
} catch (e) {
257+
} catch (e: any) {
244258
expect(e.message).toMatch(/invalid data/i);
245259
expect(e.message).toMatch("line 2");
246260
}
@@ -251,7 +265,7 @@ describe("lib/db", () => {
251265
try {
252266
await db.open();
253267
throw new Error("it did not throw");
254-
} catch (e) {
268+
} catch (e: any) {
255269
expect(e.message).toMatch(/invalid data/i);
256270
expect(e.message).toMatch("line 2");
257271
}
@@ -262,7 +276,7 @@ describe("lib/db", () => {
262276
try {
263277
await db.open();
264278
throw new Error("it did not throw");
265-
} catch (e) {
279+
} catch (e: any) {
266280
expect(e.message).toMatch(/invalid data/i);
267281
expect(e.message).toMatch("line 1");
268282
}

src/lib/db.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ export interface JsonlDBOptions<V> {
6767
*/
6868
maxBufferedCommands?: number;
6969
};
70+
71+
/**
72+
* Override in which directory the lockfile is created.
73+
* Defaults to the directory in which the DB file is located.
74+
*/
75+
lockfileDirectory?: string;
7076
}
7177

7278
/** This is the same as `fs-extra`'s WriteOptions */
@@ -102,6 +108,9 @@ export class JsonlDB<V extends unknown = unknown> {
102108
this.filename = filename;
103109
this.dumpFilename = this.filename + ".dump";
104110
this.backupFilename = this.filename + ".bak";
111+
this.lockfileName = options.lockfileDirectory
112+
? path.join(options.lockfileDirectory, path.basename(this.filename))
113+
: this.filename;
105114

106115
this.options = options;
107116
// Bind all map properties we can use directly
@@ -152,6 +161,7 @@ export class JsonlDB<V extends unknown = unknown> {
152161
public readonly filename: string;
153162
public readonly dumpFilename: string;
154163
public readonly backupFilename: string;
164+
private readonly lockfileName: string;
155165

156166
private options: JsonlDBOptions<V>;
157167

@@ -200,7 +210,8 @@ export class JsonlDB<V extends unknown = unknown> {
200210
await fs.ensureDir(path.dirname(this.filename));
201211

202212
try {
203-
await lockfile.lock(this.filename, {
213+
await fs.ensureDir(path.dirname(this.lockfileName));
214+
await lockfile.lock(this.lockfileName, {
204215
// We cannot be sure that the file exists before acquiring the lock
205216
realpath: false,
206217

@@ -215,7 +226,7 @@ export class JsonlDB<V extends unknown = unknown> {
215226
},
216227
});
217228
} catch (e) {
218-
throw new Error(`Failed to lock DB file "${this.filename}"!`);
229+
throw new Error(`Failed to lock DB file "${this.lockfileName}"!`);
219230
}
220231

221232
// If the application crashed previously, try to recover from it
@@ -784,8 +795,8 @@ export class JsonlDB<V extends unknown = unknown> {
784795

785796
// Free the lock
786797
try {
787-
if (await lockfile.check(this.filename, { realpath: false }))
788-
await lockfile.unlock(this.filename, { realpath: false });
798+
if (await lockfile.check(this.lockfileName, { realpath: false }))
799+
await lockfile.unlock(this.lockfileName, { realpath: false });
789800
} catch {
790801
// whatever - just don't crash
791802
}

0 commit comments

Comments
 (0)