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

Commit

Permalink
fix: handle concurrent writes on windows
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
achingbrain committed Sep 8, 2019
1 parent bb44723 commit d5c8e4f
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 1 deletion.
22 changes: 21 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const fs = require('fs')
const glob = require('glob')
const mkdirp = require('mkdirp')
const promisify = require('util').promisify
const writeFile = promisify(require('fast-write-atomic'))
const writeAtomic = promisify(require('fast-write-atomic'))
const path = require('path')

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

async function writeFile (path, contents) {
try {
await writeAtomic(path, contents)
} catch (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
await fsAccess(path, fs.constants.F_OK | fs.constants.W_OK)

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

throw err
}
}

/**
* A datastore backed by the file system.
*
Expand Down
15 changes: 15 additions & 0 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,19 @@ describe('FsDatastore', () => {
}
})
})

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

await Promise.all(
new Array(100).fill(0).map(() => fstore.put(key, value))
)

const res = await fstore.get(key)

expect(res).to.deep.equal(value)
})
})

0 comments on commit d5c8e4f

Please sign in to comment.