Skip to content
This repository was archived by the owner on Mar 23, 2023. It is now read-only.

Commit ba30134

Browse files
committed
fix: handle concurrent writes on windows
Windows can return EPERM errors when trying to rename temp files to files that already exist. In our case a file with a given name will always have the same content so if it's created while we are trying to also create it, we can reasonably assume it's ok to use. If we want to be more thorough we could hash the contents of the new file.
1 parent bb44723 commit ba30134

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

src/index.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const fs = require('fs')
44
const glob = require('glob')
55
const mkdirp = require('mkdirp')
66
const promisify = require('util').promisify
7-
const writeFile = promisify(require('fast-write-atomic'))
7+
const writeAtomic = promisify(require('fast-write-atomic'))
88
const path = require('path')
99

1010
const filter = require('interface-datastore').utils.filter
@@ -22,6 +22,26 @@ const fsUnlink = promisify(fs.unlink || noop)
2222
const Key = IDatastore.Key
2323
const Errors = IDatastore.Errors
2424

25+
async function writeFile (path, contents) {
26+
try {
27+
await writeAtomic(path, contents)
28+
} catch (err) {
29+
if (err.code === 'EPERM' && err.syscall === 'rename') {
30+
// fast-write-atomic writes a file to a temp location before renaming it.
31+
// On Windows, if the final file already exists this error is thrown.
32+
// No such error is thrown on Linux/Mac
33+
// Make sure we can read & write to this file
34+
await fs.access(path, fs.constants.F_OK | fs.constants.W_OK)
35+
36+
// The file was created by another context - this means there were
37+
// attempts to write the same block by two different function calls
38+
return
39+
}
40+
41+
throw err
42+
}
43+
}
44+
2545
/**
2646
* A datastore backed by the file system.
2747
*

test/index.spec.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,19 @@ describe('FsDatastore', () => {
172172
}
173173
})
174174
})
175+
176+
it('can survive concurrent writes', async () => {
177+
const dir = utils.tmpdir()
178+
const fstore = new FsStore(dir)
179+
const key = new Key('CIQGFTQ7FSI2COUXWWLOQ45VUM2GUZCGAXLWCTOKKPGTUWPXHBNIVOY')
180+
const value = Buffer.from('Hello world')
181+
182+
await Promise.all(
183+
new Array(100).fill(0).map(() => fstore.put(key, value))
184+
)
185+
186+
const res = await fstore.get(key)
187+
188+
expect(res).to.deep.equal(value)
189+
})
175190
})

0 commit comments

Comments
 (0)