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

fix: handle concurrent writes on windows #27

Merged
merged 1 commit into from
Sep 8, 2019
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
28 changes: 27 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const setImmediate = require('async/setImmediate')
const waterfall = require('async/series')
const each = require('async/each')
const mkdirp = require('mkdirp')
const writeFile = require('fast-write-atomic')
const writeAtomic = require('fast-write-atomic')
const path = require('path')

const asyncFilter = require('interface-datastore').utils.asyncFilter
Expand All @@ -19,6 +19,32 @@ const IDatastore = require('interface-datastore')
const Key = IDatastore.Key
const Errors = IDatastore.Errors

function writeFile (path, contents, callback) {
writeAtomic(path, contents, (err) => {
if (err) {
if (err.code === 'EPERM' && err.syscall === 'rename') {
// fast-write-atomic writes a file to a temp location before renaming it.
// On Windows, if the final file already exists this error is thrown.
// No such error is thrown on Linux/Mac
// Make sure we can read & write to this file
return fs.access(path, fs.constants.F_OK | fs.constants.W_OK, (err) => {
if (err) {
return callback(err)
}

// The file was created by another context - this means there were
// attempts to write the same block by two different function calls
return callback()
})
}

return callback(err)
}

callback()
})
}

/* :: export type FsInputOptions = {
createIfMissing?: bool,
errorIfExists?: bool,
Expand Down
25 changes: 25 additions & 0 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,4 +153,29 @@ describe('FsDatastore', () => {
}
})
})

it('can survive concurrent writes', (done) => {
const dir = utils.tmpdir()
const fstore = new FsStore(dir)
const key = new Key('CIQGFTQ7FSI2COUXWWLOQ45VUM2GUZCGAXLWCTOKKPGTUWPXHBNIVOY')
const value = Buffer.from('Hello world')

parallel(
new Array(100).fill(0).map(() => {
return (cb) => {
fstore.put(key, value, cb)
}
}),
(err) => {
expect(err).to.not.exist()

fstore.get(key, (err, res) => {
expect(err).to.not.exist()
expect(res).to.deep.equal(value)

done()
})
}
)
})
})