try to use copy_file_range for VHD merge
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
"readable-stream": "^3.0.6",
|
||||
"through2": "^4.0.2",
|
||||
"tmp": "^0.2.1",
|
||||
"syscall": "^0.2.0",
|
||||
"xo-remote-parser": "^0.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import execa from 'execa'
|
||||
import fs from 'fs-extra'
|
||||
import { ignoreErrors } from 'promise-toolbox'
|
||||
import { ignoreErrors, fromCallback } from 'promise-toolbox'
|
||||
import { join } from 'path'
|
||||
import { tmpdir } from 'os'
|
||||
|
||||
import LocalHandler from './local'
|
||||
import { Syscall6 } from 'syscall'
|
||||
import normalizePath from './_normalizePath'
|
||||
import { randomBytes } from 'crypto'
|
||||
|
||||
const sudoExeca = (command, args, opts) =>
|
||||
execa('sudo', [command, ...args], opts)
|
||||
|
||||
const computeRate = (hrtime: number[], size: number) => {
|
||||
const seconds = hrtime[0] + hrtime[1] / 1e9
|
||||
return size / seconds
|
||||
}
|
||||
|
||||
export default class MountHandler extends LocalHandler {
|
||||
constructor(
|
||||
remote,
|
||||
@@ -56,6 +64,71 @@ export default class MountHandler extends LocalHandler {
|
||||
return this._realPath
|
||||
}
|
||||
|
||||
async test(): Promise<Object> {
|
||||
/**
|
||||
* @returns the number of byte effectively copied, needs to be called in a loop!
|
||||
*/
|
||||
function copy_file_range(fdIn, offsetIn, fdOut, offsetOut, dataLen, flags = 0) {
|
||||
// we are stuck on linux x86_64
|
||||
function wrapOffset(offsetIn) {
|
||||
if (offsetIn == null)
|
||||
return 0
|
||||
const offsetInBuffer = new Uint32Array(2)
|
||||
new DataView(offsetInBuffer.buffer).setBigUint64(0, BigInt(offsetIn), true)
|
||||
return offsetInBuffer
|
||||
}
|
||||
|
||||
const SYS_copy_file_range = 326
|
||||
const [ret, _, errno] = Syscall6(SYS_copy_file_range, fdIn, wrapOffset(offsetIn), fdOut, wrapOffset(offsetOut), data.byteLength, 0)
|
||||
if (errno !== 0) {
|
||||
throw new Error('Error no', errno)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
const SIZE = 1024 * 1024 * 10
|
||||
const testFileName = normalizePath(`${Date.now()}.test`)
|
||||
const testFileName2 = normalizePath(`${Date.now()}__2.test`)
|
||||
const data = await fromCallback(randomBytes, SIZE)
|
||||
let step = 'write'
|
||||
try {
|
||||
const writeStart = process.hrtime()
|
||||
await this._outputFile(testFileName, data, { flags: 'wx' })
|
||||
const writeDuration = process.hrtime(writeStart)
|
||||
step = 'duplicate'
|
||||
const fd1 = await this._openFile(testFileName, 'r')
|
||||
const fd2 = await this._openFile(testFileName2, 'w')
|
||||
console.log('_openFile', fd1, fd2, data.byteLength)
|
||||
|
||||
const res = copy_file_range(fd1, 0, fd2, null, data.byteLength, 0)
|
||||
console.log('copy_file_range', res)
|
||||
await this._closeFile(fd2)
|
||||
|
||||
step = 'read'
|
||||
const readStart = process.hrtime()
|
||||
const read = await this._readFile(testFileName, { flags: 'r' })
|
||||
const readDuration = process.hrtime(readStart)
|
||||
|
||||
if (!data.equals(read)) {
|
||||
throw new Error('output and input did not match')
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
writeRate: computeRate(writeDuration, SIZE),
|
||||
readRate: computeRate(readDuration, SIZE),
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
step,
|
||||
file: testFileName,
|
||||
error: error.message || String(error),
|
||||
}
|
||||
} finally {
|
||||
ignoreErrors.call(this._unlink(testFileName))
|
||||
}
|
||||
}
|
||||
|
||||
async _sync() {
|
||||
// in case of multiple `sync`s, ensure we properly close previous keeper
|
||||
{
|
||||
|
||||
12
yarn.lock
12
yarn.lock
@@ -3974,7 +3974,7 @@ bind-property-descriptor@^1.0.0:
|
||||
dependencies:
|
||||
lodash "^4.17.4"
|
||||
|
||||
bindings@^1.5.0:
|
||||
bindings@^1.3.0, bindings@^1.5.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
|
||||
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
|
||||
@@ -12784,7 +12784,7 @@ node-gyp-build@~4.1.0:
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb"
|
||||
integrity sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==
|
||||
|
||||
node-gyp@^3.8.0:
|
||||
node-gyp@^3.7.0, node-gyp@^3.8.0:
|
||||
version "3.8.0"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
|
||||
integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==
|
||||
@@ -17313,6 +17313,14 @@ syntax-error@^1.1.1:
|
||||
dependencies:
|
||||
acorn-node "^1.2.0"
|
||||
|
||||
syscall@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/syscall/-/syscall-0.2.0.tgz#9308898495dfb5c062ea7a60c46f81f29f532ac4"
|
||||
integrity sha512-MLlgaLAMbOGKUVlqsLVYnJ4dBZmeE1nza4BVgVgGUr2dPV17tgR79JUPIUybX/EqGm1jywsXSXUPXpNbIXDVCw==
|
||||
dependencies:
|
||||
bindings "^1.3.0"
|
||||
node-gyp "^3.7.0"
|
||||
|
||||
table@^5.2.3:
|
||||
version "5.4.6"
|
||||
resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
|
||||
|
||||
Reference in New Issue
Block a user