Skip to content

Commit

Permalink
Fixed crash on random concurrent requests
Browse files Browse the repository at this point in the history
  • Loading branch information
tinchoz49 committed Jun 16, 2020
1 parent fa10329 commit fb300c2
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 33 deletions.
62 changes: 34 additions & 28 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ WriteRequest.prototype.onwrite = function (err, e) {
}

if (!err) {
this.file.updateSize(e.currentTarget.length, this.truncating)
this.file.updateSize(e.currentTarget.length)
}

if (this.truncating) {
Expand All @@ -166,6 +166,7 @@ WriteRequest.prototype.onwrite = function (err, e) {

WriteRequest.prototype.truncate = function () {
this.truncating = true
this.file.truncate()
this.writer.truncate(this.req.offset)
}

Expand All @@ -176,21 +177,23 @@ WriteRequest.prototype.lock = function () {
}

WriteRequest.prototype.run = function (req) {
var file = this.file
this.file.getWritableFile((err, file) => {
if (err) return req.callback(err)

this.req = req
if (!this.writer || this.writer.length !== file.size) return this.makeWriter()
this.req = req
if (!this.writer || this.writer.length !== file.size) return this.makeWriter()

const end = req.offset + req.size
if (end > file.size && !this.lock()) return
const end = req.offset + req.size
if (end > file.size && !this.lock()) return

if (req.offset > this.writer.length) {
if (req.offset > file.size) return this.truncate()
return this.makeWriter()
}
if (req.offset > this.writer.length) {
if (req.offset > file.size) return this.truncate()
return this.makeWriter()
}

this.writer.seek(req.offset)
this.writer.write(new Blob([req.data], TYPE))
this.writer.seek(req.offset)
this.writer.write(new Blob([req.data], TYPE))
})
}

function Mutex () {
Expand Down Expand Up @@ -246,12 +249,6 @@ ReadRequest.prototype.onread = function (err, buf) {
const req = this.req

if (err && this.retry) {
if (err.name === 'NotReadableError') {
this.file.clearFile()
this.run(req)
return
}

this.retry = false
if (this.lock(this)) {
this.file.clearFile()
Expand Down Expand Up @@ -289,44 +286,53 @@ class EntryFile {
this._lock = mutexify()
this._file = null
this._size = 0
}

get locked () {
return this._lock.locked
this._truncated = false
}

get size () {
return this._size
}

updateSize (size, truncating = false) {
if (truncating || size > this._size) {
updateSize (size) {
if (!this._truncated) {
this._size = size
}

this.clearFile()
}

truncate () {
this._truncated = true
}

clearFile () {
this._file = null
}

get (cb) {
if (this._file) {
cb(null, this._file)
return
if (this._file && !this._truncated) {
return cb(null, this._file)
}

this._lock(release => {
if (this._file) {
if (this._file && !this._truncated) {
return release(cb, null, this._file)
}

this._entry.file(file => {
this._truncated = false
this._file = file
this._size = file.size
release(cb, null, file)
}, err => release(cb, err))
})
}

getWritableFile (cb) {
if (!this._truncated) {
return cb(null, this)
}

this.get(cb)
}
}
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
"random-access-storage": "^1.3.0"
},
"devDependencies": {
"@dxos/browser-runner": "1.0.0-beta.6",
"@dxos/browser-runner": "^1.0.0-beta.9",
"random-access-test": "^1.0.0",
"standard": "^11.0.1",
"tap-finished": "0.0.1",
"tape": "^5.0.0"
"tape": "^5.0.1"
},
"scripts": {
"test": "browser-runner test.js",
"posttest": "npm run lint",
"lint": "standard",
"bench": "browser-runner bench.js"
"bench": "browser-runner bench.js --timeout 0"
},
"repository": {
"type": "git",
Expand Down
51 changes: 49 additions & 2 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,57 @@
const test = require('random-access-test')
const test = require('tape')
const randomAccessTest = require('random-access-test')
const racf = require('./')

const createStorage = (root) => (file, opts) => racf(`${root}/${file}`, opts)

const storage = createStorage('tests-' + Math.random())

test(function (name, options, callback) {
randomAccessTest(function (name, options, callback) {
callback(storage(name, options))
}, {})

test('write/read concurrent requests', async t => {
const st = storage('random')

const rand = (min, max) => Math.floor(Math.random() * max) + min

const read = (...args) => new Promise((resolve, reject) => {
st.read(...args, (err) => {
if (err) return reject(err)
resolve()
})
})

const write = (...args) => new Promise((resolve, reject) => {
st.write(...args, (err) => {
if (err) return reject(err)
resolve()
})
})

try {
await new Promise(resolve => st.open(() => resolve()))

const buf = Buffer.alloc(1)

await Promise.all([...Array(1000).keys()].map(from => {
return write(from, buf)
}))

await Promise.all([...Array(1000).keys()].map(() => {
const row = rand(0, 2)
const from = rand(0, 1000)
const to = 1

if (row === 0) {
return read(from, to)
}
return write(from, buf)
}))

t.pass('should work ok with random concurrent request')
t.end()
} catch (err) {
t.end(err)
}
})

0 comments on commit fb300c2

Please sign in to comment.